siluzan-tso-cli 1.1.44-beta.2 → 1.1.44-beta.4
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 +1 -1
- package/dist/index.js +193 -165
- package/dist/skill/AGENTS.md +16 -16
- package/dist/skill/_meta.json +2 -2
- package/dist/skill/assets/campaign-create-template.md +46 -46
- package/dist/skill/assets/market-analysis-rules.md +4 -4
- package/dist/skill/assets/pmax-create-template.md +17 -17
- package/dist/skill/assets/pmax-lead-form-template.md +12 -12
- package/dist/skill/assets/scripts/assemble-campaign-from-plan.mjs +3 -1
- package/dist/skill/references/README.md +31 -31
- package/dist/skill/references/accounts/accounts-balance-stats.md +38 -36
- package/dist/skill/references/accounts/accounts-list.md +20 -20
- package/dist/skill/references/accounts/accounts-permissions.md +27 -27
- package/dist/skill/references/accounts/accounts.md +9 -9
- package/dist/skill/references/accounts/finance.md +0 -1
- package/dist/skill/references/accounts/open-account-by-media.md +57 -58
- package/dist/skill/references/accounts/open-account-google-ui.md +18 -18
- package/dist/skill/references/analytics/account-analytics.md +33 -33
- package/dist/skill/references/analytics/google-analysis-batch.md +0 -1
- package/dist/skill/references/analytics/keyword-planner-workflows.md +15 -13
- package/dist/skill/references/analytics/market-analysis-guide.md +4 -1
- package/dist/skill/references/analytics/rag.md +7 -7
- package/dist/skill/references/analytics/website-diagnosis-guide.md +6 -6
- package/dist/skill/references/core/agent-conventions.md +54 -54
- package/dist/skill/references/core/intent-routing.md +107 -108
- package/dist/skill/references/core/setup.md +1 -0
- package/dist/skill/references/core/tips.md +0 -1
- package/dist/skill/references/core/workflows.md +15 -16
- package/dist/skill/references/google-ads/google-ads-campaign-plan.md +55 -55
- package/dist/skill/references/google-ads/google-ads-read.md +27 -12
- package/dist/skill/references/google-ads/google-ads-write.md +35 -18
- package/dist/skill/references/google-ads/google-ads.md +13 -14
- package/dist/skill/references/google-ads/rules/README.md +17 -17
- package/dist/skill/references/google-ads/rules/google-ads-account-audit.md +6 -8
- package/dist/skill/references/google-ads/rules/google-ads-compliance.md +5 -7
- package/dist/skill/references/google-ads/rules/google-ads-keyword-strategy.md +9 -9
- package/dist/skill/references/google-ads/rules/google-ads-launch-plan-template.md +16 -15
- package/dist/skill/references/google-ads/rules/google-ads-plan-source-fidelity.md +27 -27
- package/dist/skill/references/google-ads/rules/google-ads-pmax-launch-plan-template.md +41 -41
- package/dist/skill/references/operations/guard.md +24 -24
- package/dist/skill/references/operations/hosted-automation-self-control.md +15 -16
- package/dist/skill/references/operations/hosted-automation-user-catalog.md +18 -18
- package/dist/skill/references/report-templates/README.md +1 -1
- package/dist/skill/references/report-templates/bing-period-report.md +16 -16
- package/dist/skill/references/report-templates/google-ads-diagnosis.md +32 -29
- package/dist/skill/references/report-templates/google-inquiry-analysis.md +1 -1
- package/dist/skill/references/report-templates/google-period-report.md +5 -5
- package/dist/skill/references/report-templates/meta-account-diagnosis-report.md +2 -2
- package/dist/skill/references/report-templates/okki-weekly-google-client.md +0 -1
- package/dist/skill/references/report-templates/tiktok-period-report.md +6 -6
- package/dist/skill/report-templates/GoogleAdsDiagnosisReport.html +328 -238
- package/dist/skill/report-templates/README.md +1 -1
- package/dist/skill/report-templates/bing-period-report.html +10 -7
- package/dist/skill/report-templates/bing-period-report.md +16 -16
- package/dist/skill/report-templates/google-ads-diagnosis.md +32 -29
- package/dist/skill/report-templates/google-inquiry-analysis.md +1 -1
- package/dist/skill/report-templates/google-period-report.md +5 -5
- package/dist/skill/report-templates/meta-account-diagnosis-report.md +2 -2
- package/dist/skill/report-templates/okki-weekly-google-client.md +0 -1
- package/dist/skill/report-templates/tiktok-period-report.md +6 -6
- package/dist/skill/scripts/install.ps1 +1 -1
- package/dist/skill/scripts/install.sh +1 -1
- package/eval/cases/accounts-entityid-vs-mediaccustomerid.scenario.json +2 -14
- package/eval/cases/accounts-mcc-bind-inquiry.scenario.json +1 -3
- package/eval/cases/accounts-single-balance-not-bulk.scenario.json +3 -14
- package/eval/cases/budget-display-not-raw-micros.scenario.json +1 -8
- package/eval/cases/clue-meta-leads-json.scenario.json +2 -14
- package/eval/cases/clue-tiktok-leads-json.scenario.json +2 -11
- package/eval/cases/destructive-account-delink-needs-confirm.scenario.json +3 -9
- package/eval/cases/destructive-forewarning-delete-needs-confirm.scenario.json +3 -9
- package/eval/cases/destructive-invoice-apply-needs-confirm.scenario.json +3 -9
- package/eval/cases/facebook-analysis-google-section-aliases.scenario.json +2 -11
- package/eval/cases/facebook-analysis-not-google-keywords.scenario.json +3 -10
- package/eval/cases/facebook-analysis-period-default.scenario.json +2 -14
- package/eval/cases/finance-invoice-info-list.scenario.json +3 -11
- package/eval/cases/forewarning-list-google.scenario.json +3 -14
- package/eval/cases/google-ads-no-structural-without-confirm.scenario.json +2 -6
- package/eval/cases/google-analysis-keywords-route.scenario.json +2 -14
- package/eval/cases/guard-budget-circuit-all-google.scenario.json +2 -12
- package/eval/cases/guard-zero-conv-empty-spend.scenario.json +2 -12
- package/eval/cases/human-p1-multiturn.scenario.json +1 -5
- package/eval/cases/ir-p1-vs-p8-account-vs-url.scenario.json +5 -17
- package/eval/cases/ir-p3-cpa-monitor-not-p7.scenario.json +2 -19
- package/eval/cases/ir-p3-zero-inquiry-not-p7.scenario.json +3 -16
- package/eval/cases/ir-p4-period-not-diagnosis.scenario.json +3 -16
- package/eval/cases/ir-p8-meets-google-ads-requirements-with-url.scenario.json +3 -28
- package/eval/cases/ir-p8-network-diagnosis-with-url.scenario.json +3 -25
- package/eval/cases/ir-p8-website-monitor-report-with-url.scenario.json +3 -28
- package/eval/cases/ir-p8-website-test-report-no-url.scenario.json +3 -14
- package/eval/cases/ir-p9-industry-not-p8.scenario.json +3 -19
- package/eval/cases/ir-vague-report-clarify.scenario.json +3 -14
- package/eval/cases/ir-w3-vs-p8-from-website.scenario.json +2 -8
- package/eval/cases/ir-w5-keyword-planner-url-core-terms.scenario.json +2 -22
- package/eval/cases/meta-single-balance-not-bulk.scenario.json +3 -17
- package/eval/cases/no-legacy-json-flag.scenario.json +2 -6
- package/eval/cases/open-account-google-noninteractive.scenario.json +1 -3
- package/eval/cases/open-account-tiktok-license-file.scenario.json +1 -3
- package/eval/cases/optimize-list-by-account.scenario.json +3 -11
- package/eval/cases/p1-single-account-profile.scenario.json +1 -11
- package/eval/cases/p2-balance-scan-bulk.scenario.json +2 -9
- package/eval/cases/p3-accounts-digest.scenario.json +1 -5
- package/eval/cases/p3-cpa-scan-max-cpa.scenario.json +2 -14
- package/eval/cases/p4-fb-meta-period-report.scenario.json +2 -12
- package/eval/cases/p4-period-report-window.scenario.json +1 -8
- package/eval/cases/pmax-asset-group-create-with-bg.scenario.json +2 -13
- package/eval/cases/pmax-brand-edit-routing.scenario.json +3 -16
- package/eval/cases/pmax-edit-not-campaign-edit.scenario.json +2 -12
- package/eval/cases/pmax-enable-brand-guidelines.scenario.json +2 -12
- package/eval/cases/pmax-no-assets-update-brand-fields.scenario.json +3 -14
- package/eval/cases/rag-before-keyword-expand.scenario.json +1 -11
- package/eval/cases/rag-list-then-query.scenario.json +2 -14
- package/eval/cases/report-list-google.scenario.json +2 -11
- package/eval/cases/report-push-list-google.scenario.json +2 -11
- package/eval/cases/setup-login-or-env.scenario.json +1 -3
- package/eval/cases/setup-siluzan-data-permission-env.scenario.json +1 -3
- package/eval/cases/tiktok-bc-bind-inquiry.scenario.json +2 -6
- package/eval/cases/time-range-user-delegates-default.scenario.json +1 -8
- package/eval/cases/tips-json-out-filtering.scenario.json +1 -3
- package/eval/cases/tips-large-json-pagination.scenario.json +1 -3
- package/eval/cases/uj-ad-campaign-validate-before-create-stub.scenario.json +2 -11
- package/eval/cases/uj-ad-outdoor-campgear-search-plan.scenario.json +1 -3
- package/eval/cases/uj-ad-website-search-plan-table-format.scenario.json +1 -3
- package/eval/cases/uj-analytics-30d-pdf-campaign-device-geo.scenario.json +6 -18
- package/eval/cases/uj-analytics-compare-google-tiktok-last-month-roi.scenario.json +1 -8
- package/eval/cases/uj-analytics-ecommerce-industry-report.scenario.json +2 -14
- package/eval/cases/uj-analytics-google-weekly-trends-campaigns-keywords.scenario.json +2 -11
- package/eval/cases/uj-analytics-report-push-weekly-email.scenario.json +1 -3
- package/eval/cases/uj-finance-invoice-records-this-month.scenario.json +2 -11
- package/eval/cases/uj-life-newbie-siluzan-google-end-to-end.scenario.json +1 -4
- package/eval/cases/uj-ops-google-accounts-list-normal.scenario.json +2 -17
- package/eval/cases/uj-ops-google-yesterday-spend-conversions.scenario.json +2 -14
- package/eval/cases/uj-ops-pause-worst-adgroup-confirm.scenario.json +2 -6
- package/eval/cases/uj-ops-tiktok-leads-last-week.scenario.json +3 -17
- package/eval/cases/uj-patrol-cpc-spike-adgroups-over-15.scenario.json +1 -5
- package/eval/cases/uj-patrol-forewarning-create-daily-cap-3000.scenario.json +1 -3
- package/eval/cases/uj-patrol-forewarning-trigger-records.scenario.json +3 -17
- package/eval/cases/uj-patrol-google-balances-low.scenario.json +2 -11
- package/eval/cases/uj-roi-optimize-records-then-execute-cautiously.scenario.json +3 -14
- package/eval/cases/uj-roi-search-terms-add-negative-keywords.scenario.json +2 -14
- package/eval/cases/yandex-stats-use-porg-mediaccustomerid.scenario.json +3 -21
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -5471,11 +5471,7 @@ function register10(program2) {
|
|
|
5471
5471
|
"--max-cpa <n>",
|
|
5472
5472
|
"\u8FC7\u6EE4\uFF1A\u4FDD\u7559 CPA > \u6B64\u503C\u7684\u8D26\u6237\uFF1B\u6709\u6D88\u8017\u4E14\u8F6C\u5316\u4E3A 0 \u89C6\u4E3A\u547D\u4E2D\uFF08CPA \u65E0\u7A77\uFF09",
|
|
5473
5473
|
(v) => parseFloat(v)
|
|
5474
|
-
).option(
|
|
5475
|
-
"--zero-conversions",
|
|
5476
|
-
"\u8FC7\u6EE4\uFF1A\u4FDD\u7559\u533A\u95F4\u6D88\u8017 > 0 \u4E14\u8F6C\u5316 = 0 \u6216\u8F6C\u5316\u672A\u8FD4\u56DE\u7684\u8D26\u6237",
|
|
5477
|
-
false
|
|
5478
|
-
).option("--page-size <n>", "\u8D26\u6237\u6E05\u5355\u5206\u9875\u5927\u5C0F\uFF08\u9ED8\u8BA4 200\uFF0C\u4E0A\u9650 500\uFF09", (v) => parseInt(v, 10)).option("--max-pages <n>", "\u6700\u591A\u626B\u63CF\u9875\u6570\uFF08\u9ED8\u8BA4 20\uFF0C\u4E0A\u9650 200\uFF09", (v) => parseInt(v, 10)).option("-t, --token <token>", "Token\uFF08\u53EF\u9009\uFF1B\u4F18\u5148\u4E8E ~/.siluzan/config.json\uFF09").option(
|
|
5474
|
+
).option("--zero-conversions", "\u8FC7\u6EE4\uFF1A\u4FDD\u7559\u533A\u95F4\u6D88\u8017 > 0 \u4E14\u8F6C\u5316 = 0 \u6216\u8F6C\u5316\u672A\u8FD4\u56DE\u7684\u8D26\u6237", false).option("--page-size <n>", "\u8D26\u6237\u6E05\u5355\u5206\u9875\u5927\u5C0F\uFF08\u9ED8\u8BA4 200\uFF0C\u4E0A\u9650 500\uFF09", (v) => parseInt(v, 10)).option("--max-pages <n>", "\u6700\u591A\u626B\u63CF\u9875\u6570\uFF08\u9ED8\u8BA4 20\uFF0C\u4E0A\u9650 200\uFF09", (v) => parseInt(v, 10)).option("-t, --token <token>", "Token\uFF08\u53EF\u9009\uFF1B\u4F18\u5148\u4E8E ~/.siluzan/config.json\uFF09").option(
|
|
5479
5475
|
"--json-out <path>",
|
|
5480
5476
|
"\u843D\u76D8\uFF08\u76EE\u5F55\u6216 *.json \u6587\u4EF6\u8DEF\u5F84\uFF09\u5E76\u66F4\u65B0 cli-manifest[-<\u67E5\u8BE2id>].json\uFF1B\u76EE\u5F55\u6A21\u5F0F\u6587\u4EF6\u540D\u4E3A `<section>[-<\u67E5\u8BE2id>].json`\uFF1Bstdout \u4E00\u884C\u6458\u8981 JSON\uFF0C\u542B outlineFile\uFF08TS \u5F0F\u7C7B\u578B\u5728 `*.outline.txt`\uFF09",
|
|
5481
5477
|
void 0
|
|
@@ -101976,7 +101972,9 @@ var init_translate_fields = __esm({
|
|
|
101976
101972
|
function resolveDateRange(start, end) {
|
|
101977
101973
|
if (start && end) return { startDate: start, endDate: end };
|
|
101978
101974
|
if (!start && !end) return defaultChinaDateRange();
|
|
101979
|
-
console.error(
|
|
101975
|
+
console.error(
|
|
101976
|
+
"\n\u274C --start \u4E0E --end \u987B\u540C\u65F6\u4F20\u5165\uFF0C\u6216\u540C\u65F6\u7701\u7565\u4EE5\u4F7F\u7528\u9ED8\u8BA4\u8FD1 7 \u5929\uFF08\u622A\u81F3\u6628\u5929\uFF0CUTC+8\uFF09\u3002\n"
|
|
101977
|
+
);
|
|
101980
101978
|
process.exit(1);
|
|
101981
101979
|
}
|
|
101982
101980
|
function buildSearchParams(def, start, end, extras) {
|
|
@@ -102513,9 +102511,7 @@ async function mergeGoogleAnalysisSnapshotIntoReport(payload, snapshotDir) {
|
|
|
102513
102511
|
averageCpc: num2(dimensionSummaryRecord.averageCpc),
|
|
102514
102512
|
costPerConversion: num2(dimensionSummaryRecord.costPerConversion),
|
|
102515
102513
|
searchImpressionShare: num2(dimensionSummaryRecord.searchImpressionShare),
|
|
102516
|
-
searchBudgetLostImpressionShare: num2(
|
|
102517
|
-
dimensionSummaryRecord.searchBudgetLostImpressionShare
|
|
102518
|
-
),
|
|
102514
|
+
searchBudgetLostImpressionShare: num2(dimensionSummaryRecord.searchBudgetLostImpressionShare),
|
|
102519
102515
|
searchRankLostImpressionShare: num2(dimensionSummaryRecord.searchRankLostImpressionShare)
|
|
102520
102516
|
};
|
|
102521
102517
|
}
|
|
@@ -104220,10 +104216,7 @@ function registerRunCommand() {
|
|
|
104220
104216
|
).option("-t, --token <token>", "Auth Token").option(
|
|
104221
104217
|
"--start <date>",
|
|
104222
104218
|
"\u5F00\u59CB\u65E5\u671F YYYY-MM-DD\uFF08google-analysis \u53EA\u4F20\u5E74\u6708\u65E5\uFF1B\u7701\u7565=\u8FD1 7 \u5929\u622A\u81F3\u6628\u5929\uFF0C\u9ED8\u8BA4\u6309 UTC+8 \u65E5\u5386\uFF1B\u4E0E --end \u540C\u4F20\u6216\u540C\u7701\uFF09"
|
|
104223
|
-
).option(
|
|
104224
|
-
"--end <date>",
|
|
104225
|
-
"\u7ED3\u675F\u65E5\u671F YYYY-MM-DD\uFF08\u53EA\u4F20\u5E74\u6708\u65E5\uFF1B\u7701\u7565=\u6628\u5929\uFF09"
|
|
104226
|
-
).option("--start-date <date>", "\u540C --start\uFF08\u6587\u6863/Playbook \u517C\u5BB9\u522B\u540D\uFF09").option("--end-date <date>", "\u540C --end\uFF08\u6587\u6863/Playbook \u517C\u5BB9\u522B\u540D\uFF09").option(
|
|
104219
|
+
).option("--end <date>", "\u7ED3\u675F\u65E5\u671F YYYY-MM-DD\uFF08\u53EA\u4F20\u5E74\u6708\u65E5\uFF1B\u7701\u7565=\u6628\u5929\uFF09").option("--start-date <date>", "\u540C --start\uFF08\u6587\u6863/Playbook \u517C\u5BB9\u522B\u540D\uFF09").option("--end-date <date>", "\u540C --end\uFF08\u6587\u6863/Playbook \u517C\u5BB9\u522B\u540D\uFF09").option(
|
|
104227
104220
|
"--sections <list>",
|
|
104228
104221
|
"\u4EC5\u6267\u884C\u6307\u5B9A\u7EF4\u5EA6\uFF08\u9017\u53F7\u5206\u9694\uFF09\uFF0C\u5982 overview,keywords,ads\uFF1B\u7701\u7565=\u5168\u90E8 25 \u4E2A"
|
|
104229
104222
|
).option(
|
|
@@ -107182,7 +107175,9 @@ async function runBalanceScan(opts) {
|
|
|
107182
107175
|
\u274C balance-scan \u6570\u636E\u5F02\u5E38\uFF08${dataIssue.code}\uFF09\uFF1A${dataIssue.message}
|
|
107183
107176
|
`);
|
|
107184
107177
|
if (wroteJson) {
|
|
107185
|
-
console.error(
|
|
107178
|
+
console.error(
|
|
107179
|
+
" \u5DF2\u5199\u5165 --json-out\uFF08ok=false\uFF0C\u89C1 meta.dataIssue\uFF09\uFF1B\u8BF7\u4FEE\u590D\u540E\u91CD\u8DD1\uFF0C\u52FF\u5F53\u4F5C\u300C\u65E0\u9884\u8B66\u547D\u4E2D\u300D\u3002\n"
|
|
107180
|
+
);
|
|
107186
107181
|
}
|
|
107187
107182
|
process.exit(2);
|
|
107188
107183
|
}
|
|
@@ -107378,12 +107373,20 @@ function convertBatchMoneyTreeFromYuanInner(value) {
|
|
|
107378
107373
|
}
|
|
107379
107374
|
return value;
|
|
107380
107375
|
}
|
|
107381
|
-
function adgroupListUrl(googleApiUrl, account, startDate, endDate) {
|
|
107376
|
+
function adgroupListUrl(googleApiUrl, account, startDate, endDate, fresh) {
|
|
107382
107377
|
const params = new URLSearchParams();
|
|
107383
107378
|
params.set("startDate", toGoogleDate(startDate, -30));
|
|
107384
107379
|
params.set("endDate", toGoogleDate(endDate, 0));
|
|
107380
|
+
if (fresh) params.set("newest", "true");
|
|
107385
107381
|
return `${googleApiUrl}/adgroupnmanagement/v2/list/${account}?${params}`;
|
|
107386
107382
|
}
|
|
107383
|
+
function campaignListUrl(googleApiUrl, account, startDate, endDate, fresh) {
|
|
107384
|
+
const params = new URLSearchParams();
|
|
107385
|
+
params.set("startDate", toGoogleDate(startDate, -30));
|
|
107386
|
+
params.set("endDate", toGoogleDate(endDate, 0));
|
|
107387
|
+
if (fresh) params.set("newest", "true");
|
|
107388
|
+
return `${googleApiUrl}/campaignmanagement/v2/list/${account}?${params}`;
|
|
107389
|
+
}
|
|
107387
107390
|
function requireGoogleApi(config) {
|
|
107388
107391
|
if (!config.googleApiUrl) {
|
|
107389
107392
|
console.error(
|
|
@@ -107418,10 +107421,8 @@ function assertGoogleOnly(media) {
|
|
|
107418
107421
|
async function resolveGuardAccounts(opts, config) {
|
|
107419
107422
|
const filterIds = (opts.accounts ?? "").split(",").map((s) => s.trim()).filter(Boolean);
|
|
107420
107423
|
if (filterIds.length > 0) {
|
|
107421
|
-
process.stderr.write(
|
|
107422
|
-
|
|
107423
|
-
`
|
|
107424
|
-
);
|
|
107424
|
+
process.stderr.write(`\u23F3 [guard] \u5B50\u96C6\u6A21\u5F0F\uFF1A\u5BF9 -a \u6307\u5B9A\u7684 ${filterIds.length} \u4E2A\u8D26\u6237\u626B\u63CF\u3002
|
|
107425
|
+
`);
|
|
107425
107426
|
return filterIds.map((id) => ({ mediaCustomerId: id, name: null }));
|
|
107426
107427
|
}
|
|
107427
107428
|
const pageSize = Math.max(1, Math.min(500, opts.pageSize ?? 200));
|
|
@@ -107791,12 +107792,7 @@ async function runZeroConv(opts) {
|
|
|
107791
107792
|
const skipped2 = [];
|
|
107792
107793
|
let checked2 = 0;
|
|
107793
107794
|
if (level === "adgroup" || level === "both") {
|
|
107794
|
-
const groups = await fetchAdGroupsForDay(
|
|
107795
|
-
config,
|
|
107796
|
-
acc.mediaCustomerId,
|
|
107797
|
-
date,
|
|
107798
|
-
opts.verbose
|
|
107799
|
-
);
|
|
107795
|
+
const groups = await fetchAdGroupsForDay(config, acc.mediaCustomerId, date, opts.verbose);
|
|
107800
107796
|
for (const g of groups) {
|
|
107801
107797
|
if (onlyEnabled && !isEnabledStatus(g.statusV2)) continue;
|
|
107802
107798
|
checked2++;
|
|
@@ -107990,11 +107986,7 @@ function register11(program2) {
|
|
|
107990
107986
|
).requiredOption("-m, --media <type>", "\u76EE\u524D\u4EC5\u652F\u6301 Google").option(
|
|
107991
107987
|
"-a, --accounts <ids>",
|
|
107992
107988
|
"\u6307\u5B9A\u8D26\u6237 mediaCustomerId\uFF0C\u9017\u53F7\u5206\u9694\uFF1B\u7559\u7A7A\u5219\u626B\u63CF\u540D\u4E0B\u5168\u90E8 Google \u8D26\u6237"
|
|
107993
|
-
).option("--date <YYYY-MM-DD>", "\u7EDF\u8BA1\u65E5\uFF08Asia/Shanghai \u9ED8\u8BA4\u4ECA\u5929\uFF09\uFF1B\u5F3A\u5236 start=end=\u8BE5\u65E5").option("--ratio <n>", "\u9608\u503C\u7CFB\u6570\uFF1Aspend >= budget * ratio\uFF08\u9ED8\u8BA4 1.1\uFF09", (v) => parseFloat(v)).option(
|
|
107994
|
-
"--status <statusV2>",
|
|
107995
|
-
"\u53EA\u68C0\u67E5\u8BE5\u72B6\u6001\u7684\u7CFB\u5217\uFF08\u9ED8\u8BA4 Enabled\uFF1B\u4F20 * \u8868\u793A\u4E0D\u8FC7\u6EE4\uFF09",
|
|
107996
|
-
"Enabled"
|
|
107997
|
-
).option("--apply", "\u5BF9\u547D\u4E2D\u7CFB\u5217\u6267\u884C Paused\uFF08\u987B\u914D\u5408\u5168\u5C40 --commit\uFF09", false).option("--page-size <n>", "\u8D26\u6237\u6E05\u5355\u5206\u9875\u5927\u5C0F\uFF08\u9ED8\u8BA4 200\uFF09", (v) => parseInt(v, 10)).option("--max-pages <n>", "\u6700\u591A\u626B\u63CF\u9875\u6570\uFF08\u9ED8\u8BA4 20\uFF09", (v) => parseInt(v, 10)).option("-t, --token <token>", "Token\uFF08\u53EF\u9009\uFF09").option(
|
|
107989
|
+
).option("--date <YYYY-MM-DD>", "\u7EDF\u8BA1\u65E5\uFF08Asia/Shanghai \u9ED8\u8BA4\u4ECA\u5929\uFF09\uFF1B\u5F3A\u5236 start=end=\u8BE5\u65E5").option("--ratio <n>", "\u9608\u503C\u7CFB\u6570\uFF1Aspend >= budget * ratio\uFF08\u9ED8\u8BA4 1.1\uFF09", (v) => parseFloat(v)).option("--status <statusV2>", "\u53EA\u68C0\u67E5\u8BE5\u72B6\u6001\u7684\u7CFB\u5217\uFF08\u9ED8\u8BA4 Enabled\uFF1B\u4F20 * \u8868\u793A\u4E0D\u8FC7\u6EE4\uFF09", "Enabled").option("--apply", "\u5BF9\u547D\u4E2D\u7CFB\u5217\u6267\u884C Paused\uFF08\u987B\u914D\u5408\u5168\u5C40 --commit\uFF09", false).option("--page-size <n>", "\u8D26\u6237\u6E05\u5355\u5206\u9875\u5927\u5C0F\uFF08\u9ED8\u8BA4 200\uFF09", (v) => parseInt(v, 10)).option("--max-pages <n>", "\u6700\u591A\u626B\u63CF\u9875\u6570\uFF08\u9ED8\u8BA4 20\uFF09", (v) => parseInt(v, 10)).option("-t, --token <token>", "Token\uFF08\u53EF\u9009\uFF09").option(
|
|
107998
107990
|
"--json-out <path>",
|
|
107999
107991
|
"\u843D\u76D8\u76EE\u5F55\u6216 *.json\uFF1Bstdout \u4E00\u884C\u6458\u8981\uFF1B\u4EBA\u7C7B\u53EF\u8BFB\u8868\u59CB\u7EC8\u6253\u5370",
|
|
108000
107992
|
void 0
|
|
@@ -108021,15 +108013,7 @@ function register11(program2) {
|
|
|
108021
108013
|
).requiredOption("-m, --media <type>", "\u76EE\u524D\u4EC5\u652F\u6301 Google").option(
|
|
108022
108014
|
"-a, --accounts <ids>",
|
|
108023
108015
|
"\u6307\u5B9A\u8D26\u6237 mediaCustomerId\uFF0C\u9017\u53F7\u5206\u9694\uFF1B\u7559\u7A7A\u5219\u626B\u63CF\u540D\u4E0B\u5168\u90E8 Google \u8D26\u6237"
|
|
108024
|
-
).option("--date <YYYY-MM-DD>", "\u7EDF\u8BA1\u65E5\uFF08Asia/Shanghai \u9ED8\u8BA4\u4ECA\u5929\uFF09").option(
|
|
108025
|
-
"--cpa-multiple <n>",
|
|
108026
|
-
"\u500D\u6570 N\uFF1Aspend >= targetCpa \xD7 N\uFF08\u9ED8\u8BA4 3\uFF09",
|
|
108027
|
-
(v) => parseFloat(v)
|
|
108028
|
-
).option(
|
|
108029
|
-
"--level <level>",
|
|
108030
|
-
"\u626B\u63CF\u5C42\u7EA7\uFF1Aadgroup | ad | both\uFF08\u9ED8\u8BA4 adgroup\uFF09",
|
|
108031
|
-
"adgroup"
|
|
108032
|
-
).option(
|
|
108016
|
+
).option("--date <YYYY-MM-DD>", "\u7EDF\u8BA1\u65E5\uFF08Asia/Shanghai \u9ED8\u8BA4\u4ECA\u5929\uFF09").option("--cpa-multiple <n>", "\u500D\u6570 N\uFF1Aspend >= targetCpa \xD7 N\uFF08\u9ED8\u8BA4 3\uFF09", (v) => parseFloat(v)).option("--level <level>", "\u626B\u63CF\u5C42\u7EA7\uFF1Aadgroup | ad | both\uFF08\u9ED8\u8BA4 adgroup\uFF09", "adgroup").option(
|
|
108033
108017
|
"--fallback-target-cpa <yuan>",
|
|
108034
108018
|
"\u7EC4\u65E0 targetCpa\uFF08\u6216\u521B\u610F\u7EF4\uFF09\u65F6\u4F7F\u7528\u7684\u76EE\u6807 CPA\uFF08\u5143\uFF09",
|
|
108035
108019
|
(v) => parseFloat(v)
|
|
@@ -108134,14 +108118,7 @@ async function fetchStatsWindow(media, ids, config, startDate, endDate, verbose)
|
|
|
108134
108118
|
}
|
|
108135
108119
|
return items2;
|
|
108136
108120
|
}
|
|
108137
|
-
const map = await fetchOverviewMap(
|
|
108138
|
-
media,
|
|
108139
|
-
ids,
|
|
108140
|
-
config,
|
|
108141
|
-
startDate,
|
|
108142
|
-
endDate,
|
|
108143
|
-
verbose
|
|
108144
|
-
);
|
|
108121
|
+
const map = await fetchOverviewMap(media, ids, config, startDate, endDate, verbose);
|
|
108145
108122
|
return [...map.values()];
|
|
108146
108123
|
}
|
|
108147
108124
|
async function runStats(opts) {
|
|
@@ -108185,14 +108162,7 @@ async function runStats(opts) {
|
|
|
108185
108162
|
if (opts.verbose || days.length > 7) {
|
|
108186
108163
|
console.error(` \u2192 [${i + 1}/${days.length}] ${day}`);
|
|
108187
108164
|
}
|
|
108188
|
-
const dayItems = await fetchStatsWindow(
|
|
108189
|
-
opts.media,
|
|
108190
|
-
ids,
|
|
108191
|
-
config,
|
|
108192
|
-
day,
|
|
108193
|
-
day,
|
|
108194
|
-
opts.verbose
|
|
108195
|
-
);
|
|
108165
|
+
const dayItems = await fetchStatsWindow(opts.media, ids, config, day, day, opts.verbose);
|
|
108196
108166
|
for (const row of dayItems) {
|
|
108197
108167
|
items2.push({ ...row, date: day });
|
|
108198
108168
|
}
|
|
@@ -111902,7 +111872,9 @@ function resolveKeywordTexts(opts) {
|
|
|
111902
111872
|
const fromFile = opts.keywordsFile ? loadKeywordsFromFile(opts.keywordsFile) : [];
|
|
111903
111873
|
const merged = [...fromCsv, ...fromFile];
|
|
111904
111874
|
if (merged.length === 0) {
|
|
111905
|
-
throw new Error(
|
|
111875
|
+
throw new Error(
|
|
111876
|
+
"\u8BF7\u4F20 --keywords \u548C/\u6216 --keywords-file\uFF08\u591A\u8BCD\u77ED\u8BED\u76F4\u63A5\u5199\u7A7A\u683C\u5373\u53EF\uFF0C\u5982 how to make\uFF09"
|
|
111877
|
+
);
|
|
111906
111878
|
}
|
|
111907
111879
|
if (!opts.matchType) return merged;
|
|
111908
111880
|
return merged.map((k) => formatKeywordTextForMatchType(k, opts.matchType));
|
|
@@ -111930,6 +111902,9 @@ async function runAdKeywords(opts) {
|
|
|
111930
111902
|
const params = new URLSearchParams();
|
|
111931
111903
|
params.set("startDate", toGoogleDate(opts.startDate, -30));
|
|
111932
111904
|
params.set("endDate", toGoogleDate(opts.endDate, 0));
|
|
111905
|
+
if (opts.negative && opts.level) {
|
|
111906
|
+
params.set("appliedlevel", opts.level);
|
|
111907
|
+
}
|
|
111933
111908
|
const endpoint = opts.negative ? "negativekeywordmanagement" : "keywordmanagement";
|
|
111934
111909
|
const url = `${googleApiUrl}/${endpoint}/v2/list/${opts.account}?${params}`;
|
|
111935
111910
|
let data;
|
|
@@ -111977,7 +111952,8 @@ ${label}\uFF08\u8D26\u6237\uFF1A${opts.account}\uFF0C\u7B2C 1 \u9875\uFF0C\u672C
|
|
|
111977
111952
|
items2.forEach((item) => {
|
|
111978
111953
|
const kwText = Array.isArray(item["keywordText"]) ? item["keywordText"].join(", ") : String(item["text"] ?? item["keywordText"] ?? item["id"] ?? "\u2014");
|
|
111979
111954
|
const matchType = item["matchTypeV2"] ?? "\u2014";
|
|
111980
|
-
|
|
111955
|
+
const level = item["negativeLevel"] ?? "\u2014";
|
|
111956
|
+
console.log(` [${matchType}] ${kwText} id: ${String(item["id"] ?? "")} \u7EA7\u522B: ${level}`);
|
|
111981
111957
|
});
|
|
111982
111958
|
} else {
|
|
111983
111959
|
const kwW = Math.min(
|
|
@@ -112080,6 +112056,30 @@ async function runAdKeywordCreate(opts) {
|
|
|
112080
112056
|
\u2705 \u5DF2\u6DFB\u52A0 ${keywords.length} \u4E2A\u5173\u952E\u8BCD\u5230\u5E7F\u544A\u7EC4\u300C${opts.adgroupName}\u300D
|
|
112081
112057
|
`);
|
|
112082
112058
|
}
|
|
112059
|
+
function resolveNegativeKeywordLevel(opts) {
|
|
112060
|
+
if (opts.level) return opts.level;
|
|
112061
|
+
return opts.adgroupId ? "AdGroup" : "Campaign";
|
|
112062
|
+
}
|
|
112063
|
+
function buildNegativeKeywordCreateBody(level, keywords, opts) {
|
|
112064
|
+
if (level === "Account") {
|
|
112065
|
+
return { KeywordText: keywords, NegativeLevel: "Account" };
|
|
112066
|
+
}
|
|
112067
|
+
const body = {
|
|
112068
|
+
KeywordText: keywords,
|
|
112069
|
+
CampaignId: opts.campaignId,
|
|
112070
|
+
Campaign: opts.campaignName
|
|
112071
|
+
};
|
|
112072
|
+
if (level === "AdGroup") {
|
|
112073
|
+
body["AdGroupId"] = opts.adgroupId;
|
|
112074
|
+
body["AdGroup"] = opts.adgroupName ?? "";
|
|
112075
|
+
}
|
|
112076
|
+
return body;
|
|
112077
|
+
}
|
|
112078
|
+
var negativeKeywordLevelLabel = {
|
|
112079
|
+
Account: "\u8D26\u6237",
|
|
112080
|
+
Campaign: "\u5E7F\u544A\u7CFB\u5217",
|
|
112081
|
+
AdGroup: "\u5E7F\u544A\u7EC4"
|
|
112082
|
+
};
|
|
112083
112083
|
async function runAdNegativeKeywordCreate(opts) {
|
|
112084
112084
|
opts = withResolvedAdAccount(opts);
|
|
112085
112085
|
const config = loadConfig(opts.token);
|
|
@@ -112097,16 +112097,27 @@ async function runAdNegativeKeywordCreate(opts) {
|
|
|
112097
112097
|
`);
|
|
112098
112098
|
process.exit(1);
|
|
112099
112099
|
}
|
|
112100
|
-
const
|
|
112101
|
-
|
|
112102
|
-
|
|
112103
|
-
|
|
112104
|
-
|
|
112105
|
-
|
|
112106
|
-
|
|
112107
|
-
|
|
112108
|
-
|
|
112100
|
+
const level = resolveNegativeKeywordLevel(opts);
|
|
112101
|
+
if (level === "Account") {
|
|
112102
|
+
if (opts.campaignId || opts.adgroupId) {
|
|
112103
|
+
console.error(
|
|
112104
|
+
"\n\u274C --level Account \u65E0\u9700\u4E5F\u4E0D\u80FD\u4F20 --campaign-id / --adgroup-id\uFF08\u8D26\u6237\u7EA7\u5426\u8BCD\u76F4\u63A5\u751F\u6548\u4E8E\u6574\u4E2A\u8D26\u6237\uFF09\n"
|
|
112105
|
+
);
|
|
112106
|
+
process.exit(1);
|
|
112107
|
+
}
|
|
112108
|
+
} else {
|
|
112109
|
+
if (!opts.campaignId || !opts.campaignName) {
|
|
112110
|
+
console.error(`
|
|
112111
|
+
\u274C --level ${level} \u9700\u8981\u540C\u65F6\u4F20 --campaign-id \u4E0E --campaign-name
|
|
112112
|
+
`);
|
|
112113
|
+
process.exit(1);
|
|
112114
|
+
}
|
|
112115
|
+
if (level === "AdGroup" && !opts.adgroupId) {
|
|
112116
|
+
console.error("\n\u274C --level AdGroup \u9700\u8981\u4F20 --adgroup-id\n");
|
|
112117
|
+
process.exit(1);
|
|
112118
|
+
}
|
|
112109
112119
|
}
|
|
112120
|
+
const body = buildNegativeKeywordCreateBody(level, keywords, opts);
|
|
112110
112121
|
const url = `${googleApiUrl}/negativekeywordmanagement/negativekeyword/${opts.account}`;
|
|
112111
112122
|
try {
|
|
112112
112123
|
await apiFetch2(url, config, { method: "POST", body: JSON.stringify(body) }, opts.verbose);
|
|
@@ -112116,9 +112127,11 @@ async function runAdNegativeKeywordCreate(opts) {
|
|
|
112116
112127
|
`);
|
|
112117
112128
|
process.exit(1);
|
|
112118
112129
|
}
|
|
112119
|
-
console.log(
|
|
112120
|
-
|
|
112121
|
-
|
|
112130
|
+
console.log(
|
|
112131
|
+
`
|
|
112132
|
+
\u2705 \u5DF2\u5728${negativeKeywordLevelLabel[level]}\u5C42\u7EA7\u6DFB\u52A0 ${keywords.length} \u4E2A\u5426\u5B9A\u5173\u952E\u8BCD
|
|
112133
|
+
`
|
|
112134
|
+
);
|
|
112122
112135
|
}
|
|
112123
112136
|
async function runAdKeywordDelete(opts) {
|
|
112124
112137
|
opts = withResolvedAdAccount(opts);
|
|
@@ -113506,10 +113519,7 @@ async function runAdCampaigns(opts) {
|
|
|
113506
113519
|
opts = withResolvedAdAccount(opts);
|
|
113507
113520
|
const config = loadConfig(opts.token);
|
|
113508
113521
|
const googleApiUrl = requireGoogleApi(config);
|
|
113509
|
-
const
|
|
113510
|
-
params.set("startDate", toGoogleDate(opts.startDate, -30));
|
|
113511
|
-
params.set("endDate", toGoogleDate(opts.endDate, 0));
|
|
113512
|
-
const url = `${googleApiUrl}/campaignmanagement/v2/list/${opts.account}?${params}`;
|
|
113522
|
+
const url = campaignListUrl(googleApiUrl, opts.account, opts.startDate, opts.endDate, opts.fresh);
|
|
113513
113523
|
let data;
|
|
113514
113524
|
try {
|
|
113515
113525
|
data = await apiFetch2(url, config, {}, opts.verbose);
|
|
@@ -113939,10 +113949,7 @@ async function runAdGroups(opts) {
|
|
|
113939
113949
|
opts = withResolvedAdAccount(opts);
|
|
113940
113950
|
const config = loadConfig(opts.token);
|
|
113941
113951
|
const googleApiUrl = requireGoogleApi(config);
|
|
113942
|
-
const
|
|
113943
|
-
params.set("startDate", toGoogleDate(opts.startDate, -30));
|
|
113944
|
-
params.set("endDate", toGoogleDate(opts.endDate, 0));
|
|
113945
|
-
const url = `${googleApiUrl}/adgroupnmanagement/v2/list/${opts.account}?${params}`;
|
|
113952
|
+
const url = adgroupListUrl(googleApiUrl, opts.account, opts.startDate, opts.endDate, opts.fresh);
|
|
113946
113953
|
let data;
|
|
113947
113954
|
try {
|
|
113948
113955
|
data = await apiFetch2(url, config, {}, opts.verbose);
|
|
@@ -115583,7 +115590,12 @@ async function runAdDeviceBidSet(opts) {
|
|
|
115583
115590
|
// src/commands/ad/geo.ts
|
|
115584
115591
|
async function fetchGeoLocationCandidates(googleApiUrl, account, query, config, verbose) {
|
|
115585
115592
|
const url = `${googleApiUrl}/campaignmanagement/geolocations/${account}/${encodeURIComponent(query)}`;
|
|
115586
|
-
const data = await apiFetch2(
|
|
115593
|
+
const data = await apiFetch2(
|
|
115594
|
+
url,
|
|
115595
|
+
config,
|
|
115596
|
+
{},
|
|
115597
|
+
verbose
|
|
115598
|
+
);
|
|
115587
115599
|
return Array.isArray(data) ? data : data.data ?? [];
|
|
115588
115600
|
}
|
|
115589
115601
|
function geoItemDisplayName(item) {
|
|
@@ -115732,9 +115744,7 @@ async function runAdGeoResolve(opts) {
|
|
|
115732
115744
|
process.exit(1);
|
|
115733
115745
|
}
|
|
115734
115746
|
if (queries.length === 0) {
|
|
115735
|
-
console.error(
|
|
115736
|
-
'\n\u274C \u8BF7\u63D0\u4F9B --queries "Peru,Chile,..." \u6216 --from-file <names.txt|names.json>\n'
|
|
115737
|
-
);
|
|
115747
|
+
console.error('\n\u274C \u8BF7\u63D0\u4F9B --queries "Peru,Chile,..." \u6216 --from-file <names.txt|names.json>\n');
|
|
115738
115748
|
process.exit(1);
|
|
115739
115749
|
}
|
|
115740
115750
|
const config = loadConfig(opts.token);
|
|
@@ -115790,9 +115800,7 @@ async function runAdGeoResolve(opts) {
|
|
|
115790
115800
|
/** 可直接写入 campaign-create JSON */
|
|
115791
115801
|
locations,
|
|
115792
115802
|
targetedLocations,
|
|
115793
|
-
nameToId: Object.fromEntries(
|
|
115794
|
-
rows.filter((r) => r.id).map((r) => [r.query, r.id])
|
|
115795
|
-
)
|
|
115803
|
+
nameToId: Object.fromEntries(rows.filter((r) => r.id).map((r) => [r.query, r.id]))
|
|
115796
115804
|
};
|
|
115797
115805
|
if (await emitCliJsonOrSnapshot(
|
|
115798
115806
|
{ jsonOut: opts.jsonOut },
|
|
@@ -115807,9 +115815,11 @@ async function runAdGeoResolve(opts) {
|
|
|
115807
115815
|
if (failed.length > 0 && !opts.allowPartial) process.exit(1);
|
|
115808
115816
|
return;
|
|
115809
115817
|
}
|
|
115810
|
-
console.log(
|
|
115818
|
+
console.log(
|
|
115819
|
+
`
|
|
115811
115820
|
\u5730\u7406\u4F4D\u7F6E\u6279\u91CF\u89E3\u6790\uFF08${opts.account}\uFF09\uFF1A\u6210\u529F ${payload.resolved} / \u5931\u8D25 ${failed.length}
|
|
115812
|
-
`
|
|
115821
|
+
`
|
|
115822
|
+
);
|
|
115813
115823
|
for (const r of rows) {
|
|
115814
115824
|
if (r.id) {
|
|
115815
115825
|
console.log(` \u2705 ${r.query} \u2192 id:${r.id} ${r.name ?? ""} [${r.targetType ?? ""}]`);
|
|
@@ -115818,11 +115828,9 @@ async function runAdGeoResolve(opts) {
|
|
|
115818
115828
|
}
|
|
115819
115829
|
}
|
|
115820
115830
|
if (failed.length > 0) {
|
|
115821
|
-
console.error(
|
|
115822
|
-
`
|
|
115831
|
+
console.error(`
|
|
115823
115832
|
\u274C ${failed.length} \u4E2A\u5730\u540D\u672A\u89E3\u6790\uFF1B\u8BF7\u6539\u540D\u540E\u91CD\u8BD5\uFF0C\u6216\u68C0\u67E5\u62FC\u5199\uFF08\u52FF\u7F16\u9020 id\uFF09\u3002
|
|
115824
|
-
`
|
|
115825
|
-
);
|
|
115833
|
+
`);
|
|
115826
115834
|
if (!opts.allowPartial) process.exit(1);
|
|
115827
115835
|
} else {
|
|
115828
115836
|
console.log(
|
|
@@ -116089,13 +116097,7 @@ function buildExtensionRemediateCommandLine(opts) {
|
|
|
116089
116097
|
if (type === "CALLOUT") {
|
|
116090
116098
|
const text = pickString(props["Text"], props["CalloutText"]);
|
|
116091
116099
|
if (!text) return null;
|
|
116092
|
-
const parts = [
|
|
116093
|
-
...base,
|
|
116094
|
-
"callout",
|
|
116095
|
-
`-a ${accountId}`,
|
|
116096
|
-
`--text ${shellArg(text)}`,
|
|
116097
|
-
levelFlag
|
|
116098
|
-
];
|
|
116100
|
+
const parts = [...base, "callout", `-a ${accountId}`, `--text ${shellArg(text)}`, levelFlag];
|
|
116099
116101
|
if (campaignFlag) parts.push(campaignFlag);
|
|
116100
116102
|
parts.push(`--json-out ${jsonOut}`);
|
|
116101
116103
|
parts.push(`--commit ${shellArg("batch-diff remediate extension")}`);
|
|
@@ -118885,7 +118887,9 @@ function formatPmaxExtensionsAttachErrors(result) {
|
|
|
118885
118887
|
);
|
|
118886
118888
|
}
|
|
118887
118889
|
if (result.whatsapp && !result.whatsapp.ok) {
|
|
118888
|
-
msgs.push(
|
|
118890
|
+
msgs.push(
|
|
118891
|
+
`WhatsApp\u300C${result.whatsapp.label}\u300D\u5931\u8D25\uFF1A${classifyExtensionAttachError(result.whatsapp.error)}`
|
|
118892
|
+
);
|
|
118889
118893
|
}
|
|
118890
118894
|
return msgs;
|
|
118891
118895
|
}
|
|
@@ -121160,7 +121164,11 @@ function register21(program2) {
|
|
|
121160
121164
|
});
|
|
121161
121165
|
}
|
|
121162
121166
|
);
|
|
121163
|
-
adCmd.command("campaigns").alias("campaign-list").description("\u67E5\u8BE2\u5E7F\u544A\u7CFB\u5217\u5217\u8868\uFF08\u65E5\u671F\u9ED8\u8BA4\u8FD1 30 \u5929\uFF1B\u522B\u540D campaign-list\uFF09").requiredOption("-a, --account <id>", "Google \u8D26\u6237 mediaCustomerId").option("--start <date>", "\u5F00\u59CB\u65E5\u671F YYYY-MM-DD\uFF08\u9ED8\u8BA4 30 \u5929\u524D\uFF09").option("--end <date>", "\u7ED3\u675F\u65E5\u671F YYYY-MM-DD\uFF08\u9ED8\u8BA4\u4ECA\u5929\uFF09").option(
|
|
121167
|
+
adCmd.command("campaigns").alias("campaign-list").description("\u67E5\u8BE2\u5E7F\u544A\u7CFB\u5217\u5217\u8868\uFF08\u65E5\u671F\u9ED8\u8BA4\u8FD1 30 \u5929\uFF1B\u522B\u540D campaign-list\uFF09").requiredOption("-a, --account <id>", "Google \u8D26\u6237 mediaCustomerId").option("--start <date>", "\u5F00\u59CB\u65E5\u671F YYYY-MM-DD\uFF08\u9ED8\u8BA4 30 \u5929\u524D\uFF09").option("--end <date>", "\u7ED3\u675F\u65E5\u671F YYYY-MM-DD\uFF08\u9ED8\u8BA4\u4ECA\u5929\uFF09").option(
|
|
121168
|
+
"--fresh",
|
|
121169
|
+
"\u8DF3\u8FC7\u7F51\u5173 60 \u79D2\u7F13\u5B58\uFF0C\u5F3A\u5236\u62C9\u53D6\u6700\u65B0\u6570\u636E\uFF08\u7F51\u5173 newest=true\uFF09\uFF1B\u521A\u521B\u5EFA/\u7F16\u8F91\u5B8C\u60F3\u7ACB\u523B\u770B\u5230\u7ED3\u679C\u65F6\u7528",
|
|
121170
|
+
false
|
|
121171
|
+
).option("-t, --token <token>", "Auth Token").option(
|
|
121164
121172
|
"--json-out <path>",
|
|
121165
121173
|
"\u843D\u76D8\uFF08\u76EE\u5F55\u6216 *.json \u6587\u4EF6\u8DEF\u5F84\uFF09\u5E76\u66F4\u65B0 cli-manifest[-<\u67E5\u8BE2id>].json\uFF1B\u76EE\u5F55\u6A21\u5F0F\u6587\u4EF6\u540D\u4E3A `<section>[-<\u67E5\u8BE2id>].json`\uFF1Bstdout \u4E00\u884C\u6458\u8981 JSON\uFF0C\u542B outlineFile\uFF08TS \u5F0F\u7C7B\u578B\u5728 `*.outline.txt`\uFF09",
|
|
121166
121174
|
void 0
|
|
@@ -121171,12 +121179,17 @@ function register21(program2) {
|
|
|
121171
121179
|
account: opts.account,
|
|
121172
121180
|
startDate: opts.start,
|
|
121173
121181
|
endDate: opts.end,
|
|
121182
|
+
fresh: opts.fresh,
|
|
121174
121183
|
jsonOut: opts.jsonOut,
|
|
121175
121184
|
verbose: opts.verbose
|
|
121176
121185
|
});
|
|
121177
121186
|
}
|
|
121178
121187
|
);
|
|
121179
|
-
adCmd.command("groups").alias("group-list").description("\u67E5\u8BE2\u5E7F\u544A\u7EC4\u5217\u8868\uFF08\u65E5\u671F\u9ED8\u8BA4\u8FD1 30 \u5929\uFF1B\u522B\u540D group-list\uFF09").requiredOption("-a, --account <id>", "Google \u8D26\u6237 mediaCustomerId").option("--start <date>", "\u5F00\u59CB\u65E5\u671F YYYY-MM-DD\uFF08\u9ED8\u8BA4 30 \u5929\u524D\uFF09").option("--end <date>", "\u7ED3\u675F\u65E5\u671F YYYY-MM-DD\uFF08\u9ED8\u8BA4\u4ECA\u5929\uFF09").option(
|
|
121188
|
+
adCmd.command("groups").alias("group-list").description("\u67E5\u8BE2\u5E7F\u544A\u7EC4\u5217\u8868\uFF08\u65E5\u671F\u9ED8\u8BA4\u8FD1 30 \u5929\uFF1B\u522B\u540D group-list\uFF09").requiredOption("-a, --account <id>", "Google \u8D26\u6237 mediaCustomerId").option("--start <date>", "\u5F00\u59CB\u65E5\u671F YYYY-MM-DD\uFF08\u9ED8\u8BA4 30 \u5929\u524D\uFF09").option("--end <date>", "\u7ED3\u675F\u65E5\u671F YYYY-MM-DD\uFF08\u9ED8\u8BA4\u4ECA\u5929\uFF09").option(
|
|
121189
|
+
"--fresh",
|
|
121190
|
+
"\u8DF3\u8FC7\u7F51\u5173 60 \u79D2\u7F13\u5B58\uFF0C\u5F3A\u5236\u62C9\u53D6\u6700\u65B0\u6570\u636E\uFF08\u7F51\u5173 newest=true\uFF09\uFF1B\u521A\u521B\u5EFA/\u7F16\u8F91\u5B8C\u60F3\u7ACB\u523B\u770B\u5230\u7ED3\u679C\u65F6\u7528",
|
|
121191
|
+
false
|
|
121192
|
+
).option("-t, --token <token>", "Auth Token").option(
|
|
121180
121193
|
"--json-out <path>",
|
|
121181
121194
|
"\u843D\u76D8\uFF08\u76EE\u5F55\u6216 *.json \u6587\u4EF6\u8DEF\u5F84\uFF09\u5E76\u66F4\u65B0 cli-manifest[-<\u67E5\u8BE2id>].json\uFF1B\u76EE\u5F55\u6A21\u5F0F\u6587\u4EF6\u540D\u4E3A `<section>[-<\u67E5\u8BE2id>].json`\uFF1Bstdout \u4E00\u884C\u6458\u8981 JSON\uFF0C\u542B outlineFile\uFF08TS \u5F0F\u7C7B\u578B\u5728 `*.outline.txt`\uFF09",
|
|
121182
121195
|
void 0
|
|
@@ -121187,6 +121200,7 @@ function register21(program2) {
|
|
|
121187
121200
|
account: opts.account,
|
|
121188
121201
|
startDate: opts.start,
|
|
121189
121202
|
endDate: opts.end,
|
|
121203
|
+
fresh: opts.fresh,
|
|
121190
121204
|
jsonOut: opts.jsonOut,
|
|
121191
121205
|
verbose: opts.verbose
|
|
121192
121206
|
});
|
|
@@ -121209,16 +121223,26 @@ function register21(program2) {
|
|
|
121209
121223
|
});
|
|
121210
121224
|
}
|
|
121211
121225
|
);
|
|
121212
|
-
adCmd.command("keywords").alias("keyword-list").description(
|
|
121226
|
+
adCmd.command("keywords").alias("keyword-list").description(
|
|
121227
|
+
"\u67E5\u8BE2\u5173\u952E\u8BCD\u5217\u8868\uFF08--negative \u67E5\u8BE2\u5426\u5B9A\u5173\u952E\u8BCD\uFF0C\u53EF\u7528 --level \u6309\u7CFB\u5217/\u7EC4/\u8D26\u6237\u8FC7\u6EE4\uFF1B\u65E5\u671F\u9ED8\u8BA4\u8FD1 30 \u5929\uFF1B\u522B\u540D keyword-list\uFF09"
|
|
121228
|
+
).requiredOption("-a, --account <id>", "Google \u8D26\u6237 mediaCustomerId").option("--negative", "\u67E5\u8BE2\u5426\u5B9A\u5173\u952E\u8BCD", false).option(
|
|
121229
|
+
"--level <level>",
|
|
121230
|
+
"\u5426\u5B9A\u5173\u952E\u8BCD\u5C42\u7EA7\u8FC7\u6EE4\uFF1AAccount | Campaign | AdGroup\uFF08\u4EC5\u914D\u5408 --negative\uFF1B\u4E0D\u4F20\u5219\u4E09\u7EA7\u90FD\u663E\u793A\uFF09"
|
|
121231
|
+
).option("--start <date>", "\u5F00\u59CB\u65E5\u671F\uFF08YYYY-MM-DD\uFF0C\u9ED8\u8BA4 30 \u5929\u524D\uFF09").option("--end <date>", "\u7ED3\u675F\u65E5\u671F\uFF08YYYY-MM-DD\uFF0C\u9ED8\u8BA4\u4ECA\u5929\uFF09").option("-t, --token <token>", "Auth Token").option(
|
|
121213
121232
|
"--json-out <path>",
|
|
121214
121233
|
"\u843D\u76D8\uFF08\u76EE\u5F55\u6216 *.json \u6587\u4EF6\u8DEF\u5F84\uFF09\u5E76\u66F4\u65B0 cli-manifest[-<\u67E5\u8BE2id>].json\uFF1B\u76EE\u5F55\u6A21\u5F0F\u6587\u4EF6\u540D\u4E3A `<section>[-<\u67E5\u8BE2id>].json`\uFF1Bstdout \u4E00\u884C\u6458\u8981 JSON\uFF0C\u542B outlineFile\uFF08TS \u5F0F\u7C7B\u578B\u5728 `*.outline.txt`\uFF09",
|
|
121215
121234
|
void 0
|
|
121216
121235
|
).option("--verbose", "\u8BE6\u7EC6\u9519\u8BEF\u4FE1\u606F", false).action(
|
|
121217
121236
|
async (opts) => {
|
|
121237
|
+
if (opts.level && !["Account", "Campaign", "AdGroup"].includes(opts.level)) {
|
|
121238
|
+
console.error("\n\u274C --level \u53EA\u63A5\u53D7 Account | Campaign | AdGroup\n");
|
|
121239
|
+
process.exit(1);
|
|
121240
|
+
}
|
|
121218
121241
|
await runAdKeywords({
|
|
121219
121242
|
token: opts.token,
|
|
121220
121243
|
account: opts.account,
|
|
121221
121244
|
negative: opts.negative,
|
|
121245
|
+
level: opts.level,
|
|
121222
121246
|
startDate: opts.start,
|
|
121223
121247
|
endDate: opts.end,
|
|
121224
121248
|
jsonOut: opts.jsonOut,
|
|
@@ -121394,8 +121418,17 @@ function register21(program2) {
|
|
|
121394
121418
|
}
|
|
121395
121419
|
);
|
|
121396
121420
|
adCmd.command("keyword-negative-create").description(
|
|
121397
|
-
"\u6DFB\u52A0\u5426\u5B9A\u5173\u952E\u8BCD\uFF08\u7CFB\u5217/\u7EC4\u7EA7\uFF1B\u652F\u6301\u7A7A\u683C\u591A\u8BCD\u77ED\u8BED\uFF0C\u5982 how to make\uFF1B\u53EF\u7528 --keywords-file\uFF09"
|
|
121398
|
-
).requiredOption("-a, --account <id>", "Google \u8D26\u6237 mediaCustomerId").
|
|
121421
|
+
"\u6DFB\u52A0\u5426\u5B9A\u5173\u952E\u8BCD\uFF08\u7CFB\u5217/\u7EC4/\u8D26\u6237\u4E09\u7EA7\uFF1B\u652F\u6301\u7A7A\u683C\u591A\u8BCD\u77ED\u8BED\uFF0C\u5982 how to make\uFF1B\u53EF\u7528 --keywords-file\uFF09\n\n --level Account\uFF1A\u8D26\u6237\u7EA7\u5426\u8BCD\uFF08Sammamish 2026-08 \u65B0\u589E\uFF0CAccount Settings \u2192 Negative keywords\uFF09\uFF0C\u65E0\u9700 --campaign-id/--adgroup-id\u3002\n \u4E0D\u4F20 --level\uFF1A\u6309\u662F\u5426\u4F20 --adgroup-id \u81EA\u52A8\u5224\u65AD Campaign / AdGroup\uFF08\u517C\u5BB9\u65E7\u7528\u6CD5\uFF09\u3002"
|
|
121422
|
+
).requiredOption("-a, --account <id>", "Google \u8D26\u6237 mediaCustomerId").option(
|
|
121423
|
+
"--level <level>",
|
|
121424
|
+
"\u5C42\u7EA7\uFF1AAccount | Campaign | AdGroup\uFF08\u4E0D\u4F20\u5219\u6309 --adgroup-id \u81EA\u52A8\u5224\u65AD\uFF09"
|
|
121425
|
+
).option(
|
|
121426
|
+
"--campaign-id <id>",
|
|
121427
|
+
"\u5E7F\u544A\u7CFB\u5217 ID\uFF08level=Campaign/AdGroup \u5FC5\u4F20\uFF1Blevel=Account \u4E0D\u53EF\u4F20\uFF09"
|
|
121428
|
+
).option(
|
|
121429
|
+
"--campaign-name <name>",
|
|
121430
|
+
"\u5E7F\u544A\u7CFB\u5217\u540D\u79F0\uFF08level=Campaign/AdGroup \u5FC5\u4F20\uFF1Blevel=Account \u4E0D\u53EF\u4F20\uFF09"
|
|
121431
|
+
).option(
|
|
121399
121432
|
"--keywords <kw1,kw2,...>",
|
|
121400
121433
|
"\u5426\u5B9A\u8BCD\u5217\u8868\uFF0C\u9017\u53F7\u5206\u9694\uFF08\u7A7A\u683C\u591A\u8BCD\u5408\u6CD5\uFF09\uFF1B\u53EF\u4E0E --keywords-file \u5E76\u7528",
|
|
121401
121434
|
(v) => parseKeywordsCsv(v)
|
|
@@ -121405,11 +121438,19 @@ function register21(program2) {
|
|
|
121405
121438
|
).option(
|
|
121406
121439
|
"--match-type <Broad|Phrase|Exact>",
|
|
121407
121440
|
"\u7EDF\u4E00\u5339\u914D\u7C7B\u578B\uFF08Broad \u65E0\u5305\u88C5 / Phrase \u52A0\u53CC\u5F15\u53F7 / Exact \u52A0\u65B9\u62EC\u53F7\uFF09"
|
|
121408
|
-
).option(
|
|
121441
|
+
).option(
|
|
121442
|
+
"--adgroup-id <id>",
|
|
121443
|
+
"\u5E7F\u544A\u7EC4 ID\uFF08level=AdGroup \u5FC5\u4F20\uFF1B\u672A\u4F20 --level \u65F6\u6307\u5B9A\u5373\u89C6\u4E3A AdGroup \u5C42\u7EA7\uFF09"
|
|
121444
|
+
).option("--adgroup-name <name>", "\u5E7F\u544A\u7EC4\u540D\u79F0").option("-t, --token <token>", "Auth Token").option("--verbose", "\u8BE6\u7EC6\u9519\u8BEF\u4FE1\u606F", false).action(
|
|
121409
121445
|
async (opts) => {
|
|
121446
|
+
if (opts.level && !["Account", "Campaign", "AdGroup"].includes(opts.level)) {
|
|
121447
|
+
console.error("\n\u274C --level \u53EA\u63A5\u53D7 Account | Campaign | AdGroup\n");
|
|
121448
|
+
process.exit(1);
|
|
121449
|
+
}
|
|
121410
121450
|
await runAdNegativeKeywordCreate({
|
|
121411
121451
|
token: opts.token,
|
|
121412
121452
|
account: opts.account,
|
|
121453
|
+
level: opts.level,
|
|
121413
121454
|
campaignId: opts.campaignId,
|
|
121414
121455
|
campaignName: opts.campaignName,
|
|
121415
121456
|
adgroupId: opts.adgroupId,
|
|
@@ -121421,7 +121462,9 @@ function register21(program2) {
|
|
|
121421
121462
|
});
|
|
121422
121463
|
}
|
|
121423
121464
|
);
|
|
121424
|
-
adCmd.command("keyword-negative-delete").description(
|
|
121465
|
+
adCmd.command("keyword-negative-delete").description(
|
|
121466
|
+
"\u5220\u9664\u5426\u5B9A\u5173\u952E\u8BCD\uFF08\u9700\u5148\u901A\u8FC7 ad keywords --negative \u67E5\u5230 ID\uFF1B\u7CFB\u5217/\u7EC4/\u8D26\u6237\u4E09\u7EA7\u901A\u7528\uFF0C\u8D26\u6237\u7EA7\u65E0\u9700\u989D\u5916\u53C2\u6570\uFF09"
|
|
121467
|
+
).requiredOption("-a, --account <id>", "Google \u8D26\u6237 mediaCustomerId").requiredOption("--id <keywordId>", "\u5426\u5B9A\u5173\u952E\u8BCD ID").option("--start <date>", "\u5217\u8868\u67E5\u8BE2\u8D77\u59CB\u65E5\u671F YYYY-MM-DD\uFF08\u9ED8\u8BA4\u6700\u8FD1 30 \u5929\uFF09").option("--end <date>", "\u5217\u8868\u67E5\u8BE2\u7ED3\u675F\u65E5\u671F YYYY-MM-DD\uFF08\u9ED8\u8BA4\u4ECA\u5929\uFF09").option("-t, --token <token>", "Auth Token").option("--verbose", "\u8BE6\u7EC6\u9519\u8BEF\u4FE1\u606F", false).action(
|
|
121425
121468
|
async (opts) => {
|
|
121426
121469
|
await runAdNegativeKeywordDelete({
|
|
121427
121470
|
token: opts.token,
|
|
@@ -121877,7 +121920,7 @@ function register21(program2) {
|
|
|
121877
121920
|
});
|
|
121878
121921
|
}
|
|
121879
121922
|
);
|
|
121880
|
-
adCmd.command("keyword-negative-edit").description("\u7F16\u8F91\u5426\u5B9A\u5173\u952E\u8BCD\uFF08\u6587\u672C\u6216\u5339\u914D\u7C7B\u578B\uFF0C\u81F3\u5C11\u4F20\u4E00\u4E2A\u4FEE\u6539\u9879\uFF09").requiredOption("-a, --account <id>", "Google \u8D26\u6237 mediaCustomerId").requiredOption(
|
|
121923
|
+
adCmd.command("keyword-negative-edit").description("\u7F16\u8F91\u5426\u5B9A\u5173\u952E\u8BCD\uFF08\u6587\u672C\u6216\u5339\u914D\u7C7B\u578B\uFF0C\u81F3\u5C11\u4F20\u4E00\u4E2A\u4FEE\u6539\u9879\uFF1B\u7CFB\u5217/\u7EC4/\u8D26\u6237\u4E09\u7EA7\u901A\u7528\uFF09").requiredOption("-a, --account <id>", "Google \u8D26\u6237 mediaCustomerId").requiredOption(
|
|
121881
121924
|
"--id <keywordId>",
|
|
121882
121925
|
"\u5426\u5B9A\u5173\u952E\u8BCD ID\uFF08\u6765\u81EA ad keywords --negative --json-out \u2192 id\uFF09"
|
|
121883
121926
|
).option("--text <text>", "\u65B0\u5173\u952E\u8BCD\u6587\u672C").option(
|
|
@@ -126965,7 +127008,9 @@ function buildDataAvailability(opts) {
|
|
|
126965
127008
|
} else {
|
|
126966
127009
|
parts.push("\u4F18\u5148\u4F9D\u636E signals.* \u5E03\u5C14/\u8BA1\u6570\u8BC1\u636E\u5199 issue\uFF1Bsuggestion \u987B\u542B\u53EF\u6267\u884C\u52A8\u4F5C\u4E0E\u9875\u9762\u4F4D\u7F6E\u3002");
|
|
126967
127010
|
}
|
|
126968
|
-
parts.push(
|
|
127011
|
+
parts.push(
|
|
127012
|
+
"\u5B8C\u6574\u6D41\u7A0B\uFF1A\u6309 signals + website-diagnosis-rules \u2192 \u5199 diagnosis.json \u2192 website-diagnosis render\u3002"
|
|
127013
|
+
);
|
|
126969
127014
|
return {
|
|
126970
127015
|
lighthouse,
|
|
126971
127016
|
html,
|
|
@@ -126981,7 +127026,8 @@ function extractWebsiteDiagnosisSignals(opts) {
|
|
|
126981
127026
|
if (/googletagmanager\.com|gtm\.js|GTM-[A-Z0-9]+/i.test(rawHtml)) analyticsHints.push("gtm");
|
|
126982
127027
|
if (/google-analytics\.com|gtag\(|G-[A-Z0-9]+|UA-\d+/i.test(rawHtml)) analyticsHints.push("ga");
|
|
126983
127028
|
if (/facebook\.net\/.*fbevents|fbq\s*\(/i.test(rawHtml)) analyticsHints.push("meta_pixel");
|
|
126984
|
-
if (/hotjar\.com|clarity\.ms|linkedin\.com\/px/i.test(rawHtml))
|
|
127029
|
+
if (/hotjar\.com|clarity\.ms|linkedin\.com\/px/i.test(rawHtml))
|
|
127030
|
+
analyticsHints.push("other_tracker");
|
|
126985
127031
|
const html = stripTags(rawHtml);
|
|
126986
127032
|
const titleM = html.match(/<title[^>]*>([\s\S]*?)<\/title>/i);
|
|
126987
127033
|
const title = titleM?.[1]?.replace(/\s+/g, " ").trim() || null;
|
|
@@ -128508,9 +128554,7 @@ function googleAdsDiagnosisReportTemplatePath() {
|
|
|
128508
128554
|
}
|
|
128509
128555
|
function injectGoogleAdsDiagnosisData(html, payload) {
|
|
128510
128556
|
if (!html.includes(GOOGLE_ADS_DIAGNOSIS_DATA_MARKER)) {
|
|
128511
|
-
throw new Error(
|
|
128512
|
-
"\u62A5\u544A\u6A21\u677F\u7F3A\u5C11 __REPORT_DATA__ \u951A\u70B9\uFF0C\u8BF7\u66F4\u65B0 GoogleAdsDiagnosisReport.html"
|
|
128513
|
-
);
|
|
128557
|
+
throw new Error("\u62A5\u544A\u6A21\u677F\u7F3A\u5C11 __REPORT_DATA__ \u951A\u70B9\uFF0C\u8BF7\u66F4\u65B0 GoogleAdsDiagnosisReport.html");
|
|
128514
128558
|
}
|
|
128515
128559
|
const json = JSON.stringify(payload).replace(/</g, "\\u003c");
|
|
128516
128560
|
return html.replace(GOOGLE_ADS_DIAGNOSIS_DATA_MARKER, json);
|
|
@@ -128576,11 +128620,7 @@ function buildGoogleAdsDiagnosisAgentBrief(input) {
|
|
|
128576
128620
|
},
|
|
128577
128621
|
comparisonTableItemCounts: input.comparisonTableItemCounts,
|
|
128578
128622
|
agentRules: {
|
|
128579
|
-
doNotModifyItemRows: [
|
|
128580
|
-
"campaigns.items",
|
|
128581
|
-
"geographic.items",
|
|
128582
|
-
"keywords.items"
|
|
128583
|
-
],
|
|
128623
|
+
doNotModifyItemRows: ["campaigns.items", "geographic.items", "keywords.items"],
|
|
128584
128624
|
renderRestoresComparisonItemsFromCollect: "render \u9ED8\u8BA4\u540C\u76EE\u5F55 google-ads-diagnosis-collect.json \u4F1A\u4EE5 collect \u8986\u76D6\u4E0A\u8FF0 items\uFF08Agent \u53EA\u586B analysis/suggestions\uFF09",
|
|
128585
128625
|
mayEditItemFields: [
|
|
128586
128626
|
"biddingStrategy.items[].recommendationReason",
|
|
@@ -128632,7 +128672,6 @@ function createDefaultDiagnosisReportData() {
|
|
|
128632
128672
|
keywordCount: 0,
|
|
128633
128673
|
adCount: 0,
|
|
128634
128674
|
extensionCount: 0,
|
|
128635
|
-
negativeKeywordCount: 0,
|
|
128636
128675
|
countriesWithConversionsCount: 0
|
|
128637
128676
|
},
|
|
128638
128677
|
conversionGoals: { items: [] },
|
|
@@ -128887,7 +128926,14 @@ function toDailyConversionItem(row) {
|
|
|
128887
128926
|
const rawDate = String(row.date ?? "");
|
|
128888
128927
|
const date = rawDate.includes("T") ? rawDate.slice(0, 10) : rawDate.slice(0, 10);
|
|
128889
128928
|
const cpa = conversions > 0 ? cost / conversions : num(row.costPerConversion ?? row.cpa);
|
|
128890
|
-
return {
|
|
128929
|
+
return {
|
|
128930
|
+
date,
|
|
128931
|
+
cost,
|
|
128932
|
+
conversions,
|
|
128933
|
+
cpa,
|
|
128934
|
+
clicks: num(row.clicks),
|
|
128935
|
+
impressions: num(row.impressions)
|
|
128936
|
+
};
|
|
128891
128937
|
}
|
|
128892
128938
|
function loadCompanyNameFromSnap(snapDir) {
|
|
128893
128939
|
const file = path23.join(snapDir, "list-accounts-google.json");
|
|
@@ -128965,7 +129011,7 @@ function buildStructureCounts(resourceRecord, campaignRows2, keywordRows, search
|
|
|
128965
129011
|
keywordCount,
|
|
128966
129012
|
adCount: num(resourceRecord?.adCount),
|
|
128967
129013
|
extensionCount: num(resourceRecord?.extensionCount),
|
|
128968
|
-
negativeKeywordCount
|
|
129014
|
+
// collect 未拉取否词明细,不输出 negativeKeywordCount(避免报告展示 0 误导)
|
|
128969
129015
|
countriesWithConversionsCount: num(resourceRecord?.countriesWithConversionsCount),
|
|
128970
129016
|
searchTermCount: searchRows.length
|
|
128971
129017
|
};
|
|
@@ -128988,9 +129034,7 @@ function buildKeywordShareItems(rows) {
|
|
|
128988
129034
|
return {
|
|
128989
129035
|
keyword: keywordText || "\u672A\u547D\u540D",
|
|
128990
129036
|
cpc: num(row.averageCpc ?? row.cpc),
|
|
128991
|
-
matchType: String(
|
|
128992
|
-
row.keywordMatchTypeZh ?? row.keywordMatchType ?? row.matchType ?? ""
|
|
128993
|
-
),
|
|
129037
|
+
matchType: String(row.keywordMatchTypeZh ?? row.keywordMatchType ?? row.matchType ?? ""),
|
|
128994
129038
|
impressions: num(row.impressions),
|
|
128995
129039
|
clicks: num(row.clicks),
|
|
128996
129040
|
cost,
|
|
@@ -129061,12 +129105,12 @@ function buildBudgetCompetitiveness(overviewRecord, dimensionRecord) {
|
|
|
129061
129105
|
const current = asPeriodRecord(overviewRecord?.currentPeriod);
|
|
129062
129106
|
const totalCost = num(overviewRecord?.totalCost ?? current?.spend);
|
|
129063
129107
|
const activeDays = num(overviewRecord?.activeDays);
|
|
129064
|
-
const avgDaily = num(
|
|
129108
|
+
const avgDaily = num(
|
|
129109
|
+
overviewRecord?.averageDailyCost ?? (activeDays > 0 ? totalCost / activeDays : 0)
|
|
129110
|
+
);
|
|
129065
129111
|
const remainingBudget = num(overviewRecord?.remainingAccountBudget);
|
|
129066
129112
|
const utilization = remainingBudget > 0 && avgDaily > 0 ? Math.min(100, avgDaily / (avgDaily + remainingBudget / Math.max(activeDays, 1)) * 100) : avgDaily > 0 ? 85 : 0;
|
|
129067
|
-
const sis = num(
|
|
129068
|
-
dimensionRecord?.searchImpressionShare ?? current?.searchImpressionShare
|
|
129069
|
-
);
|
|
129113
|
+
const sis = num(dimensionRecord?.searchImpressionShare ?? current?.searchImpressionShare);
|
|
129070
129114
|
const budgetLost = num(
|
|
129071
129115
|
dimensionRecord?.searchBudgetLostImpressionShare ?? current?.searchBudgetLostImpressionShare
|
|
129072
129116
|
);
|
|
@@ -129126,13 +129170,12 @@ function buildGoogleAdsDiagnosisCollectPayload(opts) {
|
|
|
129126
129170
|
loadSectionSnapshot(snapDir, "resource-counts", accountId, start, end)
|
|
129127
129171
|
);
|
|
129128
129172
|
const goldRecord = sectionRecord(loadSectionSnapshot(snapDir, "gold-account", accountId, start, end)) ?? {};
|
|
129173
|
+
delete goldRecord.adsNegativeQualified;
|
|
129129
129174
|
const campaignCurrent = sectionItems(
|
|
129130
129175
|
loadSectionSnapshot(snapDir, "campaigns", accountId, start, end)
|
|
129131
129176
|
);
|
|
129132
129177
|
const adRows = sectionItems(loadSectionSnapshot(snapDir, "ads", accountId, start, end));
|
|
129133
|
-
const kwCurrent = sectionItems(
|
|
129134
|
-
loadSectionSnapshot(snapDir, "keywords", accountId, start, end)
|
|
129135
|
-
);
|
|
129178
|
+
const kwCurrent = sectionItems(loadSectionSnapshot(snapDir, "keywords", accountId, start, end));
|
|
129136
129179
|
const searchRows = sectionItems(
|
|
129137
129180
|
loadSectionSnapshot(snapDir, "search-terms", accountId, start, end)
|
|
129138
129181
|
);
|
|
@@ -129195,7 +129238,6 @@ function buildGoogleAdsDiagnosisCollectPayload(opts) {
|
|
|
129195
129238
|
keywordCount: structureCounts.keywordCount,
|
|
129196
129239
|
adCount: structureCounts.adCount,
|
|
129197
129240
|
extensionCount: structureCounts.extensionCount,
|
|
129198
|
-
negativeKeywordCount: structureCounts.negativeKeywordCount,
|
|
129199
129241
|
countriesWithConversionsCount: structureCounts.countriesWithConversionsCount
|
|
129200
129242
|
};
|
|
129201
129243
|
data.conversionGoals = {
|
|
@@ -129261,9 +129303,7 @@ function buildGoogleAdsDiagnosisCollectPayload(opts) {
|
|
|
129261
129303
|
items: geoTargeting,
|
|
129262
129304
|
...emptyModuleNarrative()
|
|
129263
129305
|
};
|
|
129264
|
-
const deviceRows = sectionItems(
|
|
129265
|
-
loadSectionSnapshot(snapDir, "devices", accountId, start, end)
|
|
129266
|
-
);
|
|
129306
|
+
const deviceRows = sectionItems(loadSectionSnapshot(snapDir, "devices", accountId, start, end));
|
|
129267
129307
|
const deviceItems = deviceRows.map(
|
|
129268
129308
|
(row) => toTargetingItem(row, "deviceType", String(row.deviceType ?? "\u672A\u77E5\u8BBE\u5907"))
|
|
129269
129309
|
);
|
|
@@ -129313,9 +129353,7 @@ function buildGoogleAdsDiagnosisCollectPayload(opts) {
|
|
|
129313
129353
|
items: buildAdCreativeItems(adRows),
|
|
129314
129354
|
...emptyModuleNarrative()
|
|
129315
129355
|
};
|
|
129316
|
-
const campaignTypes = sectionRecord(
|
|
129317
|
-
loadSectionSnapshot(snapDir, "campaign-types", accountId)
|
|
129318
|
-
);
|
|
129356
|
+
const campaignTypes = sectionRecord(loadSectionSnapshot(snapDir, "campaign-types", accountId));
|
|
129319
129357
|
const nfItems = (Array.isArray(data.newFeatures.items) ? data.newFeatures.items : []).filter((item) => String(item.strategy ?? "") !== "AiMax");
|
|
129320
129358
|
data.newFeatures = {
|
|
129321
129359
|
items: nfItems.map((item) => {
|
|
@@ -129462,9 +129500,7 @@ function previousDateRange2(start, end) {
|
|
|
129462
129500
|
}
|
|
129463
129501
|
async function resolveWebsiteUrl(opts, snapDir, accountId) {
|
|
129464
129502
|
if (opts.website?.trim()) return normalizeWebsiteUrl(opts.website);
|
|
129465
|
-
const ads = sectionItems(
|
|
129466
|
-
loadSectionSnapshot(snapDir, "ads", accountId, opts.start, opts.end)
|
|
129467
|
-
);
|
|
129503
|
+
const ads = sectionItems(loadSectionSnapshot(snapDir, "ads", accountId, opts.start, opts.end));
|
|
129468
129504
|
for (const ad of ads) {
|
|
129469
129505
|
const url = String(ad.finalurl ?? ad.destinationUrl ?? "").trim();
|
|
129470
129506
|
if (url) return normalizeWebsiteUrl(url);
|
|
@@ -129503,18 +129539,14 @@ async function runGoogleAdsDiagnosisCollect(opts) {
|
|
|
129503
129539
|
}
|
|
129504
129540
|
if (mainPartial) {
|
|
129505
129541
|
console.error(main.stderr || main.stdout);
|
|
129506
|
-
console.error(
|
|
129507
|
-
"\n\u26A0\uFE0F google-analysis \u90E8\u5206\u7EF4\u5EA6\u62C9\u53D6\u5931\u8D25\uFF0C\u5C06\u7EE7\u7EED\u62C9\u53D6\u5BF9\u6BD4\u5468\u671F\u5E76\u751F\u6210 collect\u3002\n"
|
|
129508
|
-
);
|
|
129542
|
+
console.error("\n\u26A0\uFE0F google-analysis \u90E8\u5206\u7EF4\u5EA6\u62C9\u53D6\u5931\u8D25\uFF0C\u5C06\u7EE7\u7EED\u62C9\u53D6\u5BF9\u6BD4\u5468\u671F\u5E76\u751F\u6210 collect\u3002\n");
|
|
129509
129543
|
degraded = true;
|
|
129510
129544
|
}
|
|
129511
129545
|
if (opts.fetchPreviousPeriod !== false) {
|
|
129512
129546
|
const prev = previousDateRange2(opts.start, opts.end);
|
|
129513
|
-
console.error(
|
|
129514
|
-
`
|
|
129547
|
+
console.error(`
|
|
129515
129548
|
\u{1F4E5} \u62C9\u53D6\u5BF9\u6BD4\u5468\u671F campaigns/geographic/keywords\uFF1A${prev.start} ~ ${prev.end}
|
|
129516
|
-
`
|
|
129517
|
-
);
|
|
129549
|
+
`);
|
|
129518
129550
|
const prevRun = invokeGoogleAnalysisCli({
|
|
129519
129551
|
account: accountId,
|
|
129520
129552
|
start: prev.start,
|
|
@@ -129668,17 +129700,13 @@ function validateAdCreativeItemSuggestions(data) {
|
|
|
129668
129700
|
const row = asRecord7(items2[i]);
|
|
129669
129701
|
const title = String(row?.adTitle ?? i);
|
|
129670
129702
|
if (!hasNarrativeContent(row?.suggestion ?? row?.suggestions)) {
|
|
129671
|
-
warnings.push(
|
|
129672
|
-
`adCreativeOptimization.items[${title}].suggestion \u4E3A\u7A7A\uFF08\u987B\u7531 Agent \u586B\u5199\uFF09`
|
|
129673
|
-
);
|
|
129703
|
+
warnings.push(`adCreativeOptimization.items[${title}].suggestion \u4E3A\u7A7A\uFF08\u987B\u7531 Agent \u586B\u5199\uFF09`);
|
|
129674
129704
|
}
|
|
129675
129705
|
}
|
|
129676
129706
|
return warnings;
|
|
129677
129707
|
}
|
|
129678
129708
|
function validateGoogleAdsDiagnosisContent(data) {
|
|
129679
|
-
const missingKeys = GOOGLE_ADS_DIAGNOSIS_TOP_LEVEL_KEYS.filter(
|
|
129680
|
-
(key) => data[key] === void 0
|
|
129681
|
-
);
|
|
129709
|
+
const missingKeys = GOOGLE_ADS_DIAGNOSIS_TOP_LEVEL_KEYS.filter((key) => data[key] === void 0);
|
|
129682
129710
|
const warnings = [];
|
|
129683
129711
|
const accountInfo = asRecord7(data.accountInfo);
|
|
129684
129712
|
if (accountInfo && !accountInfo.companyName && !accountInfo.period) {
|
|
@@ -129930,7 +129958,7 @@ var NARRATIVE_CONSISTENCY_RULES = [
|
|
|
129930
129958
|
id: "negative-keyword-zero-as-core-issue",
|
|
129931
129959
|
textPattern: /否定(关键)?词.{0,40}(为\s*0|数量为\s*0|为0|缺少|没有)/,
|
|
129932
129960
|
isContradicted: () => true,
|
|
129933
|
-
hint: "\u7981\u6B62\u5C06\u300C\u5426\u5B9A\u5173\u952E\u8BCD\u6570\u91CF\u4E3A 0 / \u7F3A\u5C11\u5426\u8BCD\u300D\u5199\u5165 summary.keyIssues \u6216 diagnosisOverview.disadvantages\
|
|
129961
|
+
hint: "\u672C\u62A5\u544A collect \u672A\u62C9\u53D6\u5426\u8BCD\u6570\u636E\uFF0C\u7981\u6B62\u5C06\u300C\u5426\u5B9A\u5173\u952E\u8BCD\u6570\u91CF\u4E3A 0 / \u7F3A\u5C11\u5426\u8BCD\u300D\u5199\u5165 summary.keyIssues \u6216 diagnosisOverview.disadvantages\u3002\u6709\u641C\u7D22\u8BCD\u6D6A\u8D39\u65F6\u4EC5\u5728 \xA708 \u6309\u5177\u4F53\u641C\u7D22\u8BCD\u5199\u4F18\u5316\u5EFA\u8BAE\uFF0C\u52FF\u4EE5\u5426\u8BCD\u8BA1\u6570\u4E0B\u7ED3\u8BBA\u3002"
|
|
129934
129962
|
},
|
|
129935
129963
|
{
|
|
129936
129964
|
id: "landing-page-missing-as-core-issue",
|
|
@@ -130179,7 +130207,11 @@ function registerGoogleAdsDiagnosisCommands(program2) {
|
|
|
130179
130207
|
).requiredOption("--data <file>", "Agent \u4EA7\u51FA\u7684\u8BCA\u65AD JSON\uFF08\u4E0E MarkAI sectionData \u540C\u7ED3\u6784\uFF09").option("--out <file>", "\u8F93\u51FA HTML \u8DEF\u5F84\uFF08\u9ED8\u8BA4\u540C --data \u76EE\u5F55\uFF09").option(
|
|
130180
130208
|
"--collect <file>",
|
|
130181
130209
|
"collect JSON \u8DEF\u5F84\uFF08\u9ED8\u8BA4\u540C --data \u76EE\u5F55\u7684 google-ads-diagnosis-collect.json\uFF0C\u7528\u4E8E\u6062\u590D\u5BF9\u6BD4\u8868 items\uFF09"
|
|
130182
|
-
).option(
|
|
130210
|
+
).option(
|
|
130211
|
+
"--no-merge-collect",
|
|
130212
|
+
"\u4E0D\u4ECE collect \u81EA\u52A8\u6062\u590D campaigns/geographic/keywords.items",
|
|
130213
|
+
false
|
|
130214
|
+
).option("--lenient", "\u7ED3\u6784\u8B66\u544A\uFF08\u5982\u7F3A analysis\uFF09\u65F6\u4E0D\u963B\u65AD\u6E32\u67D3", false).action(
|
|
130183
130215
|
async (opts) => {
|
|
130184
130216
|
await runGoogleAdsDiagnosisRender({
|
|
130185
130217
|
dataFile: opts.data,
|
|
@@ -131477,12 +131509,7 @@ async function mergeMetaDiagnosisSnapshotIntoReport(payload, snapshotDir) {
|
|
|
131477
131509
|
const tables = { ...payload.tables ?? {} };
|
|
131478
131510
|
{
|
|
131479
131511
|
const rows = asRows(sectionMap.get("ad-sets")?.adGroups).map(
|
|
131480
|
-
(r) => pickMetricExtras(r, [
|
|
131481
|
-
"adGroupId",
|
|
131482
|
-
"adGroupName",
|
|
131483
|
-
"campaignName",
|
|
131484
|
-
"spendPercentage"
|
|
131485
|
-
])
|
|
131512
|
+
(r) => pickMetricExtras(r, ["adGroupId", "adGroupName", "campaignName", "spendPercentage"])
|
|
131486
131513
|
);
|
|
131487
131514
|
if (rows.length > 0) tables.adSets = rows;
|
|
131488
131515
|
}
|
|
@@ -132307,10 +132334,7 @@ async function fetchBingOverviewPayload(config, id, startDate, endDate, verbose)
|
|
|
132307
132334
|
endDate,
|
|
132308
132335
|
verbose
|
|
132309
132336
|
);
|
|
132310
|
-
return normalizeBingSectionPayload(
|
|
132311
|
-
syncOverviewBalanceFields(withPrevious),
|
|
132312
|
-
"overview"
|
|
132313
|
-
);
|
|
132337
|
+
return normalizeBingSectionPayload(syncOverviewBalanceFields(withPrevious), "overview");
|
|
132314
132338
|
}
|
|
132315
132339
|
async function enrichBingOverviewPreviousPeriod(config, accountId, record2, startDate, endDate, verbose) {
|
|
132316
132340
|
if (!isBingPeriodBlockEmpty(record2.previousPeriod)) {
|
|
@@ -133520,7 +133544,11 @@ function mapAudienceMerged(payload) {
|
|
|
133520
133544
|
return {
|
|
133521
133545
|
audienceGender: mapAudienceList(data?.gender, "gender", "gender"),
|
|
133522
133546
|
audienceAge: mapAudienceList(data?.age, "age", "ageRange"),
|
|
133523
|
-
audienceInterest: mapAudienceList(
|
|
133547
|
+
audienceInterest: mapAudienceList(
|
|
133548
|
+
data?.interest_category,
|
|
133549
|
+
"interest_category",
|
|
133550
|
+
"interestCategory"
|
|
133551
|
+
)
|
|
133524
133552
|
};
|
|
133525
133553
|
}
|
|
133526
133554
|
async function mergeTikTokAnalysisSnapshotIntoReport(payload, snapshotDir) {
|