siluzan-tso-cli 1.1.44-beta.3 → 1.1.44
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 -2
- package/dist/index.js +209 -163
- 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 +21 -21
- package/dist/skill/references/accounts/accounts-permissions.md +29 -29
- package/dist/skill/references/accounts/accounts.md +9 -9
- package/dist/skill/references/accounts/finance.md +1 -2
- package/dist/skill/references/accounts/open-account-by-media.md +58 -59
- package/dist/skill/references/accounts/open-account-google-ui.md +19 -19
- 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 +8 -8
- package/dist/skill/references/analytics/reporting.md +5 -5
- 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 +6 -5
- 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 +30 -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/misc/tso-home.md +2 -2
- 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 +26 -23
- 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 +42 -13
- 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 +26 -23
- 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 +3 -3
- package/dist/skill/scripts/install.sh +3 -3
- 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
|
@@ -4026,7 +4026,7 @@ var DEFAULT_API_BASE;
|
|
|
4026
4026
|
var init_defaults = __esm({
|
|
4027
4027
|
"src/config/defaults.ts"() {
|
|
4028
4028
|
"use strict";
|
|
4029
|
-
DEFAULT_API_BASE = "https://tso-api
|
|
4029
|
+
DEFAULT_API_BASE = "https://tso-api.siluzan.com";
|
|
4030
4030
|
}
|
|
4031
4031
|
});
|
|
4032
4032
|
|
|
@@ -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));
|
|
@@ -111918,6 +111890,9 @@ function firstKeywordTextFromRecord(k) {
|
|
|
111918
111890
|
}
|
|
111919
111891
|
return "";
|
|
111920
111892
|
}
|
|
111893
|
+
function toNegativeKeywordAppliedLevelQuery(level) {
|
|
111894
|
+
return level === "AdGroup" ? "Ad group" : level;
|
|
111895
|
+
}
|
|
111921
111896
|
function keywordRowToYuan(item) {
|
|
111922
111897
|
const { maxCPC: rawMaxCpc, ...rest } = item;
|
|
111923
111898
|
if (rawMaxCpc == null) return item;
|
|
@@ -111930,6 +111905,9 @@ async function runAdKeywords(opts) {
|
|
|
111930
111905
|
const params = new URLSearchParams();
|
|
111931
111906
|
params.set("startDate", toGoogleDate(opts.startDate, -30));
|
|
111932
111907
|
params.set("endDate", toGoogleDate(opts.endDate, 0));
|
|
111908
|
+
if (opts.negative && opts.level) {
|
|
111909
|
+
params.set("appliedlevel", toNegativeKeywordAppliedLevelQuery(opts.level));
|
|
111910
|
+
}
|
|
111933
111911
|
const endpoint = opts.negative ? "negativekeywordmanagement" : "keywordmanagement";
|
|
111934
111912
|
const url = `${googleApiUrl}/${endpoint}/v2/list/${opts.account}?${params}`;
|
|
111935
111913
|
let data;
|
|
@@ -111977,7 +111955,8 @@ ${label}\uFF08\u8D26\u6237\uFF1A${opts.account}\uFF0C\u7B2C 1 \u9875\uFF0C\u672C
|
|
|
111977
111955
|
items2.forEach((item) => {
|
|
111978
111956
|
const kwText = Array.isArray(item["keywordText"]) ? item["keywordText"].join(", ") : String(item["text"] ?? item["keywordText"] ?? item["id"] ?? "\u2014");
|
|
111979
111957
|
const matchType = item["matchTypeV2"] ?? "\u2014";
|
|
111980
|
-
|
|
111958
|
+
const level = item["negativeLevel"] ?? "\u2014";
|
|
111959
|
+
console.log(` [${matchType}] ${kwText} id: ${String(item["id"] ?? "")} \u7EA7\u522B: ${level}`);
|
|
111981
111960
|
});
|
|
111982
111961
|
} else {
|
|
111983
111962
|
const kwW = Math.min(
|
|
@@ -112080,6 +112059,30 @@ async function runAdKeywordCreate(opts) {
|
|
|
112080
112059
|
\u2705 \u5DF2\u6DFB\u52A0 ${keywords.length} \u4E2A\u5173\u952E\u8BCD\u5230\u5E7F\u544A\u7EC4\u300C${opts.adgroupName}\u300D
|
|
112081
112060
|
`);
|
|
112082
112061
|
}
|
|
112062
|
+
function resolveNegativeKeywordLevel(opts) {
|
|
112063
|
+
if (opts.level) return opts.level;
|
|
112064
|
+
return opts.adgroupId ? "AdGroup" : "Campaign";
|
|
112065
|
+
}
|
|
112066
|
+
function buildNegativeKeywordCreateBody(level, keywords, opts) {
|
|
112067
|
+
if (level === "Account") {
|
|
112068
|
+
return { KeywordText: keywords, NegativeLevel: "Account" };
|
|
112069
|
+
}
|
|
112070
|
+
const body = {
|
|
112071
|
+
KeywordText: keywords,
|
|
112072
|
+
CampaignId: opts.campaignId,
|
|
112073
|
+
Campaign: opts.campaignName
|
|
112074
|
+
};
|
|
112075
|
+
if (level === "AdGroup") {
|
|
112076
|
+
body["AdGroupId"] = opts.adgroupId;
|
|
112077
|
+
body["AdGroup"] = opts.adgroupName ?? "";
|
|
112078
|
+
}
|
|
112079
|
+
return body;
|
|
112080
|
+
}
|
|
112081
|
+
var negativeKeywordLevelLabel = {
|
|
112082
|
+
Account: "\u8D26\u6237",
|
|
112083
|
+
Campaign: "\u5E7F\u544A\u7CFB\u5217",
|
|
112084
|
+
AdGroup: "\u5E7F\u544A\u7EC4"
|
|
112085
|
+
};
|
|
112083
112086
|
async function runAdNegativeKeywordCreate(opts) {
|
|
112084
112087
|
opts = withResolvedAdAccount(opts);
|
|
112085
112088
|
const config = loadConfig(opts.token);
|
|
@@ -112097,16 +112100,27 @@ async function runAdNegativeKeywordCreate(opts) {
|
|
|
112097
112100
|
`);
|
|
112098
112101
|
process.exit(1);
|
|
112099
112102
|
}
|
|
112100
|
-
const
|
|
112101
|
-
|
|
112102
|
-
|
|
112103
|
-
|
|
112104
|
-
|
|
112105
|
-
|
|
112106
|
-
|
|
112107
|
-
|
|
112108
|
-
|
|
112103
|
+
const level = resolveNegativeKeywordLevel(opts);
|
|
112104
|
+
if (level === "Account") {
|
|
112105
|
+
if (opts.campaignId || opts.adgroupId) {
|
|
112106
|
+
console.error(
|
|
112107
|
+
"\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"
|
|
112108
|
+
);
|
|
112109
|
+
process.exit(1);
|
|
112110
|
+
}
|
|
112111
|
+
} else {
|
|
112112
|
+
if (!opts.campaignId || !opts.campaignName) {
|
|
112113
|
+
console.error(`
|
|
112114
|
+
\u274C --level ${level} \u9700\u8981\u540C\u65F6\u4F20 --campaign-id \u4E0E --campaign-name
|
|
112115
|
+
`);
|
|
112116
|
+
process.exit(1);
|
|
112117
|
+
}
|
|
112118
|
+
if (level === "AdGroup" && !opts.adgroupId) {
|
|
112119
|
+
console.error("\n\u274C --level AdGroup \u9700\u8981\u4F20 --adgroup-id\n");
|
|
112120
|
+
process.exit(1);
|
|
112121
|
+
}
|
|
112109
112122
|
}
|
|
112123
|
+
const body = buildNegativeKeywordCreateBody(level, keywords, opts);
|
|
112110
112124
|
const url = `${googleApiUrl}/negativekeywordmanagement/negativekeyword/${opts.account}`;
|
|
112111
112125
|
try {
|
|
112112
112126
|
await apiFetch2(url, config, { method: "POST", body: JSON.stringify(body) }, opts.verbose);
|
|
@@ -112116,9 +112130,11 @@ async function runAdNegativeKeywordCreate(opts) {
|
|
|
112116
112130
|
`);
|
|
112117
112131
|
process.exit(1);
|
|
112118
112132
|
}
|
|
112119
|
-
console.log(
|
|
112120
|
-
|
|
112121
|
-
|
|
112133
|
+
console.log(
|
|
112134
|
+
`
|
|
112135
|
+
\u2705 \u5DF2\u5728${negativeKeywordLevelLabel[level]}\u5C42\u7EA7\u6DFB\u52A0 ${keywords.length} \u4E2A\u5426\u5B9A\u5173\u952E\u8BCD
|
|
112136
|
+
`
|
|
112137
|
+
);
|
|
112122
112138
|
}
|
|
112123
112139
|
async function runAdKeywordDelete(opts) {
|
|
112124
112140
|
opts = withResolvedAdAccount(opts);
|
|
@@ -112271,7 +112287,21 @@ async function runAdNegativeKeywordEdit(opts) {
|
|
|
112271
112287
|
}
|
|
112272
112288
|
const url = `${googleApiUrl}/negativekeywordmanagement/negativekeyword/${opts.account}/${opts.id}`;
|
|
112273
112289
|
try {
|
|
112274
|
-
|
|
112290
|
+
const raw = await apiFetch2(
|
|
112291
|
+
url,
|
|
112292
|
+
config,
|
|
112293
|
+
{ method: "PUT", body: JSON.stringify(body) },
|
|
112294
|
+
opts.verbose
|
|
112295
|
+
);
|
|
112296
|
+
const returned = raw && typeof raw === "object" && !Array.isArray(raw) ? raw : Array.isArray(raw) && raw[0] && typeof raw[0] === "object" ? raw[0] : null;
|
|
112297
|
+
const returnedId = returned && typeof returned["id"] === "string" ? returned["id"] : void 0;
|
|
112298
|
+
if (returnedId && returnedId !== opts.id) {
|
|
112299
|
+
console.log(
|
|
112300
|
+
`
|
|
112301
|
+
\u2139\uFE0F \u7F51\u5173\u8FD4\u56DE\u7684\u5426\u5B9A\u5173\u952E\u8BCD id \u5DF2\u53D8\u66F4\u4E3A ${returnedId}\uFF08\u8BF7\u6C42\u65F6 id \u4E3A ${opts.id}\uFF09\uFF0C\u540E\u7EED\u67E5\u8BE2/\u5220\u9664\u8BF7\u4F7F\u7528\u65B0 id\u3002
|
|
112302
|
+
`
|
|
112303
|
+
);
|
|
112304
|
+
}
|
|
112275
112305
|
} catch (err) {
|
|
112276
112306
|
console.error(`
|
|
112277
112307
|
\u274C \u7F16\u8F91\u5426\u5B9A\u5173\u952E\u8BCD\u5931\u8D25\uFF1A${err instanceof Error ? err.message : String(err)}
|
|
@@ -113506,10 +113536,7 @@ async function runAdCampaigns(opts) {
|
|
|
113506
113536
|
opts = withResolvedAdAccount(opts);
|
|
113507
113537
|
const config = loadConfig(opts.token);
|
|
113508
113538
|
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}`;
|
|
113539
|
+
const url = campaignListUrl(googleApiUrl, opts.account, opts.startDate, opts.endDate, opts.fresh);
|
|
113513
113540
|
let data;
|
|
113514
113541
|
try {
|
|
113515
113542
|
data = await apiFetch2(url, config, {}, opts.verbose);
|
|
@@ -113939,10 +113966,7 @@ async function runAdGroups(opts) {
|
|
|
113939
113966
|
opts = withResolvedAdAccount(opts);
|
|
113940
113967
|
const config = loadConfig(opts.token);
|
|
113941
113968
|
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}`;
|
|
113969
|
+
const url = adgroupListUrl(googleApiUrl, opts.account, opts.startDate, opts.endDate, opts.fresh);
|
|
113946
113970
|
let data;
|
|
113947
113971
|
try {
|
|
113948
113972
|
data = await apiFetch2(url, config, {}, opts.verbose);
|
|
@@ -115583,7 +115607,12 @@ async function runAdDeviceBidSet(opts) {
|
|
|
115583
115607
|
// src/commands/ad/geo.ts
|
|
115584
115608
|
async function fetchGeoLocationCandidates(googleApiUrl, account, query, config, verbose) {
|
|
115585
115609
|
const url = `${googleApiUrl}/campaignmanagement/geolocations/${account}/${encodeURIComponent(query)}`;
|
|
115586
|
-
const data = await apiFetch2(
|
|
115610
|
+
const data = await apiFetch2(
|
|
115611
|
+
url,
|
|
115612
|
+
config,
|
|
115613
|
+
{},
|
|
115614
|
+
verbose
|
|
115615
|
+
);
|
|
115587
115616
|
return Array.isArray(data) ? data : data.data ?? [];
|
|
115588
115617
|
}
|
|
115589
115618
|
function geoItemDisplayName(item) {
|
|
@@ -115732,9 +115761,7 @@ async function runAdGeoResolve(opts) {
|
|
|
115732
115761
|
process.exit(1);
|
|
115733
115762
|
}
|
|
115734
115763
|
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
|
-
);
|
|
115764
|
+
console.error('\n\u274C \u8BF7\u63D0\u4F9B --queries "Peru,Chile,..." \u6216 --from-file <names.txt|names.json>\n');
|
|
115738
115765
|
process.exit(1);
|
|
115739
115766
|
}
|
|
115740
115767
|
const config = loadConfig(opts.token);
|
|
@@ -115790,9 +115817,7 @@ async function runAdGeoResolve(opts) {
|
|
|
115790
115817
|
/** 可直接写入 campaign-create JSON */
|
|
115791
115818
|
locations,
|
|
115792
115819
|
targetedLocations,
|
|
115793
|
-
nameToId: Object.fromEntries(
|
|
115794
|
-
rows.filter((r) => r.id).map((r) => [r.query, r.id])
|
|
115795
|
-
)
|
|
115820
|
+
nameToId: Object.fromEntries(rows.filter((r) => r.id).map((r) => [r.query, r.id]))
|
|
115796
115821
|
};
|
|
115797
115822
|
if (await emitCliJsonOrSnapshot(
|
|
115798
115823
|
{ jsonOut: opts.jsonOut },
|
|
@@ -115807,9 +115832,11 @@ async function runAdGeoResolve(opts) {
|
|
|
115807
115832
|
if (failed.length > 0 && !opts.allowPartial) process.exit(1);
|
|
115808
115833
|
return;
|
|
115809
115834
|
}
|
|
115810
|
-
console.log(
|
|
115835
|
+
console.log(
|
|
115836
|
+
`
|
|
115811
115837
|
\u5730\u7406\u4F4D\u7F6E\u6279\u91CF\u89E3\u6790\uFF08${opts.account}\uFF09\uFF1A\u6210\u529F ${payload.resolved} / \u5931\u8D25 ${failed.length}
|
|
115812
|
-
`
|
|
115838
|
+
`
|
|
115839
|
+
);
|
|
115813
115840
|
for (const r of rows) {
|
|
115814
115841
|
if (r.id) {
|
|
115815
115842
|
console.log(` \u2705 ${r.query} \u2192 id:${r.id} ${r.name ?? ""} [${r.targetType ?? ""}]`);
|
|
@@ -115818,11 +115845,9 @@ async function runAdGeoResolve(opts) {
|
|
|
115818
115845
|
}
|
|
115819
115846
|
}
|
|
115820
115847
|
if (failed.length > 0) {
|
|
115821
|
-
console.error(
|
|
115822
|
-
`
|
|
115848
|
+
console.error(`
|
|
115823
115849
|
\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
|
-
);
|
|
115850
|
+
`);
|
|
115826
115851
|
if (!opts.allowPartial) process.exit(1);
|
|
115827
115852
|
} else {
|
|
115828
115853
|
console.log(
|
|
@@ -116089,13 +116114,7 @@ function buildExtensionRemediateCommandLine(opts) {
|
|
|
116089
116114
|
if (type === "CALLOUT") {
|
|
116090
116115
|
const text = pickString(props["Text"], props["CalloutText"]);
|
|
116091
116116
|
if (!text) return null;
|
|
116092
|
-
const parts = [
|
|
116093
|
-
...base,
|
|
116094
|
-
"callout",
|
|
116095
|
-
`-a ${accountId}`,
|
|
116096
|
-
`--text ${shellArg(text)}`,
|
|
116097
|
-
levelFlag
|
|
116098
|
-
];
|
|
116117
|
+
const parts = [...base, "callout", `-a ${accountId}`, `--text ${shellArg(text)}`, levelFlag];
|
|
116099
116118
|
if (campaignFlag) parts.push(campaignFlag);
|
|
116100
116119
|
parts.push(`--json-out ${jsonOut}`);
|
|
116101
116120
|
parts.push(`--commit ${shellArg("batch-diff remediate extension")}`);
|
|
@@ -118885,7 +118904,9 @@ function formatPmaxExtensionsAttachErrors(result) {
|
|
|
118885
118904
|
);
|
|
118886
118905
|
}
|
|
118887
118906
|
if (result.whatsapp && !result.whatsapp.ok) {
|
|
118888
|
-
msgs.push(
|
|
118907
|
+
msgs.push(
|
|
118908
|
+
`WhatsApp\u300C${result.whatsapp.label}\u300D\u5931\u8D25\uFF1A${classifyExtensionAttachError(result.whatsapp.error)}`
|
|
118909
|
+
);
|
|
118889
118910
|
}
|
|
118890
118911
|
return msgs;
|
|
118891
118912
|
}
|
|
@@ -121160,7 +121181,11 @@ function register21(program2) {
|
|
|
121160
121181
|
});
|
|
121161
121182
|
}
|
|
121162
121183
|
);
|
|
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(
|
|
121184
|
+
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(
|
|
121185
|
+
"--fresh",
|
|
121186
|
+
"\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",
|
|
121187
|
+
false
|
|
121188
|
+
).option("-t, --token <token>", "Auth Token").option(
|
|
121164
121189
|
"--json-out <path>",
|
|
121165
121190
|
"\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
121191
|
void 0
|
|
@@ -121171,12 +121196,17 @@ function register21(program2) {
|
|
|
121171
121196
|
account: opts.account,
|
|
121172
121197
|
startDate: opts.start,
|
|
121173
121198
|
endDate: opts.end,
|
|
121199
|
+
fresh: opts.fresh,
|
|
121174
121200
|
jsonOut: opts.jsonOut,
|
|
121175
121201
|
verbose: opts.verbose
|
|
121176
121202
|
});
|
|
121177
121203
|
}
|
|
121178
121204
|
);
|
|
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(
|
|
121205
|
+
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(
|
|
121206
|
+
"--fresh",
|
|
121207
|
+
"\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",
|
|
121208
|
+
false
|
|
121209
|
+
).option("-t, --token <token>", "Auth Token").option(
|
|
121180
121210
|
"--json-out <path>",
|
|
121181
121211
|
"\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
121212
|
void 0
|
|
@@ -121187,6 +121217,7 @@ function register21(program2) {
|
|
|
121187
121217
|
account: opts.account,
|
|
121188
121218
|
startDate: opts.start,
|
|
121189
121219
|
endDate: opts.end,
|
|
121220
|
+
fresh: opts.fresh,
|
|
121190
121221
|
jsonOut: opts.jsonOut,
|
|
121191
121222
|
verbose: opts.verbose
|
|
121192
121223
|
});
|
|
@@ -121209,16 +121240,26 @@ function register21(program2) {
|
|
|
121209
121240
|
});
|
|
121210
121241
|
}
|
|
121211
121242
|
);
|
|
121212
|
-
adCmd.command("keywords").alias("keyword-list").description(
|
|
121243
|
+
adCmd.command("keywords").alias("keyword-list").description(
|
|
121244
|
+
"\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"
|
|
121245
|
+
).requiredOption("-a, --account <id>", "Google \u8D26\u6237 mediaCustomerId").option("--negative", "\u67E5\u8BE2\u5426\u5B9A\u5173\u952E\u8BCD", false).option(
|
|
121246
|
+
"--level <level>",
|
|
121247
|
+
"\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"
|
|
121248
|
+
).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
121249
|
"--json-out <path>",
|
|
121214
121250
|
"\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
121251
|
void 0
|
|
121216
121252
|
).option("--verbose", "\u8BE6\u7EC6\u9519\u8BEF\u4FE1\u606F", false).action(
|
|
121217
121253
|
async (opts) => {
|
|
121254
|
+
if (opts.level && !["Account", "Campaign", "AdGroup"].includes(opts.level)) {
|
|
121255
|
+
console.error("\n\u274C --level \u53EA\u63A5\u53D7 Account | Campaign | AdGroup\n");
|
|
121256
|
+
process.exit(1);
|
|
121257
|
+
}
|
|
121218
121258
|
await runAdKeywords({
|
|
121219
121259
|
token: opts.token,
|
|
121220
121260
|
account: opts.account,
|
|
121221
121261
|
negative: opts.negative,
|
|
121262
|
+
level: opts.level,
|
|
121222
121263
|
startDate: opts.start,
|
|
121223
121264
|
endDate: opts.end,
|
|
121224
121265
|
jsonOut: opts.jsonOut,
|
|
@@ -121394,8 +121435,17 @@ function register21(program2) {
|
|
|
121394
121435
|
}
|
|
121395
121436
|
);
|
|
121396
121437
|
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").
|
|
121438
|
+
"\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"
|
|
121439
|
+
).requiredOption("-a, --account <id>", "Google \u8D26\u6237 mediaCustomerId").option(
|
|
121440
|
+
"--level <level>",
|
|
121441
|
+
"\u5C42\u7EA7\uFF1AAccount | Campaign | AdGroup\uFF08\u4E0D\u4F20\u5219\u6309 --adgroup-id \u81EA\u52A8\u5224\u65AD\uFF09"
|
|
121442
|
+
).option(
|
|
121443
|
+
"--campaign-id <id>",
|
|
121444
|
+
"\u5E7F\u544A\u7CFB\u5217 ID\uFF08level=Campaign/AdGroup \u5FC5\u4F20\uFF1Blevel=Account \u4E0D\u53EF\u4F20\uFF09"
|
|
121445
|
+
).option(
|
|
121446
|
+
"--campaign-name <name>",
|
|
121447
|
+
"\u5E7F\u544A\u7CFB\u5217\u540D\u79F0\uFF08level=Campaign/AdGroup \u5FC5\u4F20\uFF1Blevel=Account \u4E0D\u53EF\u4F20\uFF09"
|
|
121448
|
+
).option(
|
|
121399
121449
|
"--keywords <kw1,kw2,...>",
|
|
121400
121450
|
"\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
121451
|
(v) => parseKeywordsCsv(v)
|
|
@@ -121405,11 +121455,19 @@ function register21(program2) {
|
|
|
121405
121455
|
).option(
|
|
121406
121456
|
"--match-type <Broad|Phrase|Exact>",
|
|
121407
121457
|
"\u7EDF\u4E00\u5339\u914D\u7C7B\u578B\uFF08Broad \u65E0\u5305\u88C5 / Phrase \u52A0\u53CC\u5F15\u53F7 / Exact \u52A0\u65B9\u62EC\u53F7\uFF09"
|
|
121408
|
-
).option(
|
|
121458
|
+
).option(
|
|
121459
|
+
"--adgroup-id <id>",
|
|
121460
|
+
"\u5E7F\u544A\u7EC4 ID\uFF08level=AdGroup \u5FC5\u4F20\uFF1B\u672A\u4F20 --level \u65F6\u6307\u5B9A\u5373\u89C6\u4E3A AdGroup \u5C42\u7EA7\uFF09"
|
|
121461
|
+
).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
121462
|
async (opts) => {
|
|
121463
|
+
if (opts.level && !["Account", "Campaign", "AdGroup"].includes(opts.level)) {
|
|
121464
|
+
console.error("\n\u274C --level \u53EA\u63A5\u53D7 Account | Campaign | AdGroup\n");
|
|
121465
|
+
process.exit(1);
|
|
121466
|
+
}
|
|
121410
121467
|
await runAdNegativeKeywordCreate({
|
|
121411
121468
|
token: opts.token,
|
|
121412
121469
|
account: opts.account,
|
|
121470
|
+
level: opts.level,
|
|
121413
121471
|
campaignId: opts.campaignId,
|
|
121414
121472
|
campaignName: opts.campaignName,
|
|
121415
121473
|
adgroupId: opts.adgroupId,
|
|
@@ -121421,7 +121479,9 @@ function register21(program2) {
|
|
|
121421
121479
|
});
|
|
121422
121480
|
}
|
|
121423
121481
|
);
|
|
121424
|
-
adCmd.command("keyword-negative-delete").description(
|
|
121482
|
+
adCmd.command("keyword-negative-delete").description(
|
|
121483
|
+
"\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"
|
|
121484
|
+
).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
121485
|
async (opts) => {
|
|
121426
121486
|
await runAdNegativeKeywordDelete({
|
|
121427
121487
|
token: opts.token,
|
|
@@ -121877,7 +121937,7 @@ function register21(program2) {
|
|
|
121877
121937
|
});
|
|
121878
121938
|
}
|
|
121879
121939
|
);
|
|
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(
|
|
121940
|
+
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
121941
|
"--id <keywordId>",
|
|
121882
121942
|
"\u5426\u5B9A\u5173\u952E\u8BCD ID\uFF08\u6765\u81EA ad keywords --negative --json-out \u2192 id\uFF09"
|
|
121883
121943
|
).option("--text <text>", "\u65B0\u5173\u952E\u8BCD\u6587\u672C").option(
|
|
@@ -126965,7 +127025,9 @@ function buildDataAvailability(opts) {
|
|
|
126965
127025
|
} else {
|
|
126966
127026
|
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
127027
|
}
|
|
126968
|
-
parts.push(
|
|
127028
|
+
parts.push(
|
|
127029
|
+
"\u5B8C\u6574\u6D41\u7A0B\uFF1A\u6309 signals + website-diagnosis-rules \u2192 \u5199 diagnosis.json \u2192 website-diagnosis render\u3002"
|
|
127030
|
+
);
|
|
126969
127031
|
return {
|
|
126970
127032
|
lighthouse,
|
|
126971
127033
|
html,
|
|
@@ -126981,7 +127043,8 @@ function extractWebsiteDiagnosisSignals(opts) {
|
|
|
126981
127043
|
if (/googletagmanager\.com|gtm\.js|GTM-[A-Z0-9]+/i.test(rawHtml)) analyticsHints.push("gtm");
|
|
126982
127044
|
if (/google-analytics\.com|gtag\(|G-[A-Z0-9]+|UA-\d+/i.test(rawHtml)) analyticsHints.push("ga");
|
|
126983
127045
|
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))
|
|
127046
|
+
if (/hotjar\.com|clarity\.ms|linkedin\.com\/px/i.test(rawHtml))
|
|
127047
|
+
analyticsHints.push("other_tracker");
|
|
126985
127048
|
const html = stripTags(rawHtml);
|
|
126986
127049
|
const titleM = html.match(/<title[^>]*>([\s\S]*?)<\/title>/i);
|
|
126987
127050
|
const title = titleM?.[1]?.replace(/\s+/g, " ").trim() || null;
|
|
@@ -128508,9 +128571,7 @@ function googleAdsDiagnosisReportTemplatePath() {
|
|
|
128508
128571
|
}
|
|
128509
128572
|
function injectGoogleAdsDiagnosisData(html, payload) {
|
|
128510
128573
|
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
|
-
);
|
|
128574
|
+
throw new Error("\u62A5\u544A\u6A21\u677F\u7F3A\u5C11 __REPORT_DATA__ \u951A\u70B9\uFF0C\u8BF7\u66F4\u65B0 GoogleAdsDiagnosisReport.html");
|
|
128514
128575
|
}
|
|
128515
128576
|
const json = JSON.stringify(payload).replace(/</g, "\\u003c");
|
|
128516
128577
|
return html.replace(GOOGLE_ADS_DIAGNOSIS_DATA_MARKER, json);
|
|
@@ -128576,11 +128637,7 @@ function buildGoogleAdsDiagnosisAgentBrief(input) {
|
|
|
128576
128637
|
},
|
|
128577
128638
|
comparisonTableItemCounts: input.comparisonTableItemCounts,
|
|
128578
128639
|
agentRules: {
|
|
128579
|
-
doNotModifyItemRows: [
|
|
128580
|
-
"campaigns.items",
|
|
128581
|
-
"geographic.items",
|
|
128582
|
-
"keywords.items"
|
|
128583
|
-
],
|
|
128640
|
+
doNotModifyItemRows: ["campaigns.items", "geographic.items", "keywords.items"],
|
|
128584
128641
|
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
128642
|
mayEditItemFields: [
|
|
128586
128643
|
"biddingStrategy.items[].recommendationReason",
|
|
@@ -128886,7 +128943,14 @@ function toDailyConversionItem(row) {
|
|
|
128886
128943
|
const rawDate = String(row.date ?? "");
|
|
128887
128944
|
const date = rawDate.includes("T") ? rawDate.slice(0, 10) : rawDate.slice(0, 10);
|
|
128888
128945
|
const cpa = conversions > 0 ? cost / conversions : num(row.costPerConversion ?? row.cpa);
|
|
128889
|
-
return {
|
|
128946
|
+
return {
|
|
128947
|
+
date,
|
|
128948
|
+
cost,
|
|
128949
|
+
conversions,
|
|
128950
|
+
cpa,
|
|
128951
|
+
clicks: num(row.clicks),
|
|
128952
|
+
impressions: num(row.impressions)
|
|
128953
|
+
};
|
|
128890
128954
|
}
|
|
128891
128955
|
function loadCompanyNameFromSnap(snapDir) {
|
|
128892
128956
|
const file = path23.join(snapDir, "list-accounts-google.json");
|
|
@@ -128987,9 +129051,7 @@ function buildKeywordShareItems(rows) {
|
|
|
128987
129051
|
return {
|
|
128988
129052
|
keyword: keywordText || "\u672A\u547D\u540D",
|
|
128989
129053
|
cpc: num(row.averageCpc ?? row.cpc),
|
|
128990
|
-
matchType: String(
|
|
128991
|
-
row.keywordMatchTypeZh ?? row.keywordMatchType ?? row.matchType ?? ""
|
|
128992
|
-
),
|
|
129054
|
+
matchType: String(row.keywordMatchTypeZh ?? row.keywordMatchType ?? row.matchType ?? ""),
|
|
128993
129055
|
impressions: num(row.impressions),
|
|
128994
129056
|
clicks: num(row.clicks),
|
|
128995
129057
|
cost,
|
|
@@ -129060,12 +129122,12 @@ function buildBudgetCompetitiveness(overviewRecord, dimensionRecord) {
|
|
|
129060
129122
|
const current = asPeriodRecord(overviewRecord?.currentPeriod);
|
|
129061
129123
|
const totalCost = num(overviewRecord?.totalCost ?? current?.spend);
|
|
129062
129124
|
const activeDays = num(overviewRecord?.activeDays);
|
|
129063
|
-
const avgDaily = num(
|
|
129125
|
+
const avgDaily = num(
|
|
129126
|
+
overviewRecord?.averageDailyCost ?? (activeDays > 0 ? totalCost / activeDays : 0)
|
|
129127
|
+
);
|
|
129064
129128
|
const remainingBudget = num(overviewRecord?.remainingAccountBudget);
|
|
129065
129129
|
const utilization = remainingBudget > 0 && avgDaily > 0 ? Math.min(100, avgDaily / (avgDaily + remainingBudget / Math.max(activeDays, 1)) * 100) : avgDaily > 0 ? 85 : 0;
|
|
129066
|
-
const sis = num(
|
|
129067
|
-
dimensionRecord?.searchImpressionShare ?? current?.searchImpressionShare
|
|
129068
|
-
);
|
|
129130
|
+
const sis = num(dimensionRecord?.searchImpressionShare ?? current?.searchImpressionShare);
|
|
129069
129131
|
const budgetLost = num(
|
|
129070
129132
|
dimensionRecord?.searchBudgetLostImpressionShare ?? current?.searchBudgetLostImpressionShare
|
|
129071
129133
|
);
|
|
@@ -129130,9 +129192,7 @@ function buildGoogleAdsDiagnosisCollectPayload(opts) {
|
|
|
129130
129192
|
loadSectionSnapshot(snapDir, "campaigns", accountId, start, end)
|
|
129131
129193
|
);
|
|
129132
129194
|
const adRows = sectionItems(loadSectionSnapshot(snapDir, "ads", accountId, start, end));
|
|
129133
|
-
const kwCurrent = sectionItems(
|
|
129134
|
-
loadSectionSnapshot(snapDir, "keywords", accountId, start, end)
|
|
129135
|
-
);
|
|
129195
|
+
const kwCurrent = sectionItems(loadSectionSnapshot(snapDir, "keywords", accountId, start, end));
|
|
129136
129196
|
const searchRows = sectionItems(
|
|
129137
129197
|
loadSectionSnapshot(snapDir, "search-terms", accountId, start, end)
|
|
129138
129198
|
);
|
|
@@ -129260,9 +129320,7 @@ function buildGoogleAdsDiagnosisCollectPayload(opts) {
|
|
|
129260
129320
|
items: geoTargeting,
|
|
129261
129321
|
...emptyModuleNarrative()
|
|
129262
129322
|
};
|
|
129263
|
-
const deviceRows = sectionItems(
|
|
129264
|
-
loadSectionSnapshot(snapDir, "devices", accountId, start, end)
|
|
129265
|
-
);
|
|
129323
|
+
const deviceRows = sectionItems(loadSectionSnapshot(snapDir, "devices", accountId, start, end));
|
|
129266
129324
|
const deviceItems = deviceRows.map(
|
|
129267
129325
|
(row) => toTargetingItem(row, "deviceType", String(row.deviceType ?? "\u672A\u77E5\u8BBE\u5907"))
|
|
129268
129326
|
);
|
|
@@ -129312,9 +129370,7 @@ function buildGoogleAdsDiagnosisCollectPayload(opts) {
|
|
|
129312
129370
|
items: buildAdCreativeItems(adRows),
|
|
129313
129371
|
...emptyModuleNarrative()
|
|
129314
129372
|
};
|
|
129315
|
-
const campaignTypes = sectionRecord(
|
|
129316
|
-
loadSectionSnapshot(snapDir, "campaign-types", accountId)
|
|
129317
|
-
);
|
|
129373
|
+
const campaignTypes = sectionRecord(loadSectionSnapshot(snapDir, "campaign-types", accountId));
|
|
129318
129374
|
const nfItems = (Array.isArray(data.newFeatures.items) ? data.newFeatures.items : []).filter((item) => String(item.strategy ?? "") !== "AiMax");
|
|
129319
129375
|
data.newFeatures = {
|
|
129320
129376
|
items: nfItems.map((item) => {
|
|
@@ -129461,9 +129517,7 @@ function previousDateRange2(start, end) {
|
|
|
129461
129517
|
}
|
|
129462
129518
|
async function resolveWebsiteUrl(opts, snapDir, accountId) {
|
|
129463
129519
|
if (opts.website?.trim()) return normalizeWebsiteUrl(opts.website);
|
|
129464
|
-
const ads = sectionItems(
|
|
129465
|
-
loadSectionSnapshot(snapDir, "ads", accountId, opts.start, opts.end)
|
|
129466
|
-
);
|
|
129520
|
+
const ads = sectionItems(loadSectionSnapshot(snapDir, "ads", accountId, opts.start, opts.end));
|
|
129467
129521
|
for (const ad of ads) {
|
|
129468
129522
|
const url = String(ad.finalurl ?? ad.destinationUrl ?? "").trim();
|
|
129469
129523
|
if (url) return normalizeWebsiteUrl(url);
|
|
@@ -129502,18 +129556,14 @@ async function runGoogleAdsDiagnosisCollect(opts) {
|
|
|
129502
129556
|
}
|
|
129503
129557
|
if (mainPartial) {
|
|
129504
129558
|
console.error(main.stderr || main.stdout);
|
|
129505
|
-
console.error(
|
|
129506
|
-
"\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"
|
|
129507
|
-
);
|
|
129559
|
+
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");
|
|
129508
129560
|
degraded = true;
|
|
129509
129561
|
}
|
|
129510
129562
|
if (opts.fetchPreviousPeriod !== false) {
|
|
129511
129563
|
const prev = previousDateRange2(opts.start, opts.end);
|
|
129512
|
-
console.error(
|
|
129513
|
-
`
|
|
129564
|
+
console.error(`
|
|
129514
129565
|
\u{1F4E5} \u62C9\u53D6\u5BF9\u6BD4\u5468\u671F campaigns/geographic/keywords\uFF1A${prev.start} ~ ${prev.end}
|
|
129515
|
-
`
|
|
129516
|
-
);
|
|
129566
|
+
`);
|
|
129517
129567
|
const prevRun = invokeGoogleAnalysisCli({
|
|
129518
129568
|
account: accountId,
|
|
129519
129569
|
start: prev.start,
|
|
@@ -129667,17 +129717,13 @@ function validateAdCreativeItemSuggestions(data) {
|
|
|
129667
129717
|
const row = asRecord7(items2[i]);
|
|
129668
129718
|
const title = String(row?.adTitle ?? i);
|
|
129669
129719
|
if (!hasNarrativeContent(row?.suggestion ?? row?.suggestions)) {
|
|
129670
|
-
warnings.push(
|
|
129671
|
-
`adCreativeOptimization.items[${title}].suggestion \u4E3A\u7A7A\uFF08\u987B\u7531 Agent \u586B\u5199\uFF09`
|
|
129672
|
-
);
|
|
129720
|
+
warnings.push(`adCreativeOptimization.items[${title}].suggestion \u4E3A\u7A7A\uFF08\u987B\u7531 Agent \u586B\u5199\uFF09`);
|
|
129673
129721
|
}
|
|
129674
129722
|
}
|
|
129675
129723
|
return warnings;
|
|
129676
129724
|
}
|
|
129677
129725
|
function validateGoogleAdsDiagnosisContent(data) {
|
|
129678
|
-
const missingKeys = GOOGLE_ADS_DIAGNOSIS_TOP_LEVEL_KEYS.filter(
|
|
129679
|
-
(key) => data[key] === void 0
|
|
129680
|
-
);
|
|
129726
|
+
const missingKeys = GOOGLE_ADS_DIAGNOSIS_TOP_LEVEL_KEYS.filter((key) => data[key] === void 0);
|
|
129681
129727
|
const warnings = [];
|
|
129682
129728
|
const accountInfo = asRecord7(data.accountInfo);
|
|
129683
129729
|
if (accountInfo && !accountInfo.companyName && !accountInfo.period) {
|
|
@@ -130178,7 +130224,11 @@ function registerGoogleAdsDiagnosisCommands(program2) {
|
|
|
130178
130224
|
).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(
|
|
130179
130225
|
"--collect <file>",
|
|
130180
130226
|
"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"
|
|
130181
|
-
).option(
|
|
130227
|
+
).option(
|
|
130228
|
+
"--no-merge-collect",
|
|
130229
|
+
"\u4E0D\u4ECE collect \u81EA\u52A8\u6062\u590D campaigns/geographic/keywords.items",
|
|
130230
|
+
false
|
|
130231
|
+
).option("--lenient", "\u7ED3\u6784\u8B66\u544A\uFF08\u5982\u7F3A analysis\uFF09\u65F6\u4E0D\u963B\u65AD\u6E32\u67D3", false).action(
|
|
130182
130232
|
async (opts) => {
|
|
130183
130233
|
await runGoogleAdsDiagnosisRender({
|
|
130184
130234
|
dataFile: opts.data,
|
|
@@ -131476,12 +131526,7 @@ async function mergeMetaDiagnosisSnapshotIntoReport(payload, snapshotDir) {
|
|
|
131476
131526
|
const tables = { ...payload.tables ?? {} };
|
|
131477
131527
|
{
|
|
131478
131528
|
const rows = asRows(sectionMap.get("ad-sets")?.adGroups).map(
|
|
131479
|
-
(r) => pickMetricExtras(r, [
|
|
131480
|
-
"adGroupId",
|
|
131481
|
-
"adGroupName",
|
|
131482
|
-
"campaignName",
|
|
131483
|
-
"spendPercentage"
|
|
131484
|
-
])
|
|
131529
|
+
(r) => pickMetricExtras(r, ["adGroupId", "adGroupName", "campaignName", "spendPercentage"])
|
|
131485
131530
|
);
|
|
131486
131531
|
if (rows.length > 0) tables.adSets = rows;
|
|
131487
131532
|
}
|
|
@@ -132306,10 +132351,7 @@ async function fetchBingOverviewPayload(config, id, startDate, endDate, verbose)
|
|
|
132306
132351
|
endDate,
|
|
132307
132352
|
verbose
|
|
132308
132353
|
);
|
|
132309
|
-
return normalizeBingSectionPayload(
|
|
132310
|
-
syncOverviewBalanceFields(withPrevious),
|
|
132311
|
-
"overview"
|
|
132312
|
-
);
|
|
132354
|
+
return normalizeBingSectionPayload(syncOverviewBalanceFields(withPrevious), "overview");
|
|
132313
132355
|
}
|
|
132314
132356
|
async function enrichBingOverviewPreviousPeriod(config, accountId, record2, startDate, endDate, verbose) {
|
|
132315
132357
|
if (!isBingPeriodBlockEmpty(record2.previousPeriod)) {
|
|
@@ -133519,7 +133561,11 @@ function mapAudienceMerged(payload) {
|
|
|
133519
133561
|
return {
|
|
133520
133562
|
audienceGender: mapAudienceList(data?.gender, "gender", "gender"),
|
|
133521
133563
|
audienceAge: mapAudienceList(data?.age, "age", "ageRange"),
|
|
133522
|
-
audienceInterest: mapAudienceList(
|
|
133564
|
+
audienceInterest: mapAudienceList(
|
|
133565
|
+
data?.interest_category,
|
|
133566
|
+
"interest_category",
|
|
133567
|
+
"interestCategory"
|
|
133568
|
+
)
|
|
133523
133569
|
};
|
|
133524
133570
|
}
|
|
133525
133571
|
async function mergeTikTokAnalysisSnapshotIntoReport(payload, snapshotDir) {
|