siluzan-tso-cli 1.1.39 → 1.1.40-beta.1
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 +2 -1
- package/dist/index.js +897 -194
- package/dist/skill/SKILL.md +2 -1
- package/dist/skill/_meta.json +2 -2
- package/dist/skill/references/accounts/accounts-list.md +1 -1
- package/dist/skill/references/accounts/accounts-permissions.md +2 -2
- package/dist/skill/references/accounts/finance.md +1 -1
- package/dist/skill/references/accounts/open-account-by-media.md +72 -68
- package/dist/skill/references/accounts/open-account-google-ui.md +30 -27
- package/dist/skill/references/analytics/rag.md +1 -1
- package/dist/skill/references/analytics/reporting.md +5 -5
- package/dist/skill/references/core/agent-conventions.md +5 -3
- package/dist/skill/references/core/setup.md +5 -5
- package/dist/skill/references/core/workflows.md +7 -6
- package/dist/skill/references/misc/tso-home.md +2 -2
- package/dist/skill/references/operations/guard.md +94 -0
- package/dist/skill/references/operations/hosted-automation-self-control.md +35 -15
- package/dist/skill/references/operations/hosted-automation-user-catalog.md +5 -5
- package/dist/skill/references/report-templates/bing-period-report.md +3 -3
- package/dist/skill/references/report-templates/google-period-report.md +1 -1
- package/dist/skill/references/report-templates/meta-account-diagnosis-report.md +1 -1
- package/dist/skill/references/report-templates/meta-period-report.md +2 -2
- package/dist/skill/references/report-templates/tiktok-period-report.md +1 -1
- package/dist/skill/report-templates/bing-period-report.md +3 -3
- package/dist/skill/report-templates/google-period-report.md +1 -1
- package/dist/skill/report-templates/meta-account-diagnosis-report.md +1 -1
- package/dist/skill/report-templates/meta-period-report.md +2 -2
- package/dist/skill/report-templates/tiktok-period-report.md +1 -1
- package/dist/skill/scripts/install.ps1 +3 -3
- package/dist/skill/scripts/install.sh +3 -3
- package/eval/cases/guard-budget-circuit-all-google.scenario.json +25 -0
- package/eval/cases/guard-zero-conv-empty-spend.scenario.json +21 -0
- package/eval/stub-fixtures/guard-budget-circuit.json +36 -0
- package/eval/stub-fixtures/guard-zero-conv.json +41 -0
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -3937,7 +3937,7 @@ var DEFAULT_API_BASE;
|
|
|
3937
3937
|
var init_defaults = __esm({
|
|
3938
3938
|
"src/config/defaults.ts"() {
|
|
3939
3939
|
"use strict";
|
|
3940
|
-
DEFAULT_API_BASE = "https://tso-api.siluzan.com";
|
|
3940
|
+
DEFAULT_API_BASE = "https://tso-api-ci.siluzan.com";
|
|
3941
3941
|
}
|
|
3942
3942
|
});
|
|
3943
3943
|
|
|
@@ -102283,15 +102283,17 @@ async function mergeGoogleAnalysisSnapshotIntoReport(payload, snapshotDir) {
|
|
|
102283
102283
|
costPerConversion: num2(currentPeriod.costPerConversion)
|
|
102284
102284
|
} : null);
|
|
102285
102285
|
if (currentKpiSource) {
|
|
102286
|
-
if (
|
|
102287
|
-
if (
|
|
102288
|
-
if (
|
|
102289
|
-
if (
|
|
102290
|
-
if (
|
|
102291
|
-
if (
|
|
102292
|
-
if (
|
|
102293
|
-
|
|
102294
|
-
|
|
102286
|
+
if (currentKpiSource.cost != null) kpis.cost = currentKpiSource.cost;
|
|
102287
|
+
if (currentKpiSource.impressions != null) kpis.impressions = currentKpiSource.impressions;
|
|
102288
|
+
if (currentKpiSource.clicks != null) kpis.clicks = currentKpiSource.clicks;
|
|
102289
|
+
if (currentKpiSource.conversions != null) kpis.conversions = currentKpiSource.conversions;
|
|
102290
|
+
if (currentKpiSource.ctr != null) kpis.ctr = currentKpiSource.ctr;
|
|
102291
|
+
if (currentKpiSource.averageCpc != null) kpis.averageCpc = currentKpiSource.averageCpc;
|
|
102292
|
+
if (currentKpiSource.costPerConversion != null) {
|
|
102293
|
+
kpis.costPerConversion = currentKpiSource.costPerConversion;
|
|
102294
|
+
}
|
|
102295
|
+
}
|
|
102296
|
+
if (previousPeriod) {
|
|
102295
102297
|
kpis.previousPeriod = {
|
|
102296
102298
|
cost: num2(previousPeriod.spend),
|
|
102297
102299
|
impressions: num2(previousPeriod.impressions),
|
|
@@ -102303,7 +102305,7 @@ async function mergeGoogleAnalysisSnapshotIntoReport(payload, snapshotDir) {
|
|
|
102303
102305
|
};
|
|
102304
102306
|
}
|
|
102305
102307
|
const charts = { ...payload.charts ?? {} };
|
|
102306
|
-
|
|
102308
|
+
{
|
|
102307
102309
|
const dailyRows = items(sectionMap.get("daily-metrics"));
|
|
102308
102310
|
if (dailyRows.length > 0) {
|
|
102309
102311
|
const sorted = [...dailyRows].sort(
|
|
@@ -102318,7 +102320,7 @@ async function mergeGoogleAnalysisSnapshotIntoReport(payload, snapshotDir) {
|
|
|
102318
102320
|
}
|
|
102319
102321
|
}
|
|
102320
102322
|
const tables = { ...payload.tables ?? {} };
|
|
102321
|
-
|
|
102323
|
+
{
|
|
102322
102324
|
const summary = record(sectionMap.get("dimension-summary"));
|
|
102323
102325
|
if (summary) {
|
|
102324
102326
|
tables.dimensionSummary = {
|
|
@@ -102335,19 +102337,19 @@ async function mergeGoogleAnalysisSnapshotIntoReport(payload, snapshotDir) {
|
|
|
102335
102337
|
};
|
|
102336
102338
|
}
|
|
102337
102339
|
}
|
|
102338
|
-
if (
|
|
102340
|
+
if (campaignRows2.length > 0) {
|
|
102339
102341
|
tables.campaigns = campaignRows2.map((r) => ({
|
|
102340
102342
|
...pickMetricRow(r, ["campaignName", "campaignStatusDisplay", "channelTypeV2"]),
|
|
102341
102343
|
budgetAmountYuan: r.budgetAmountYuan ?? null
|
|
102342
102344
|
}));
|
|
102343
102345
|
}
|
|
102344
|
-
|
|
102346
|
+
{
|
|
102345
102347
|
const rows = items(sectionMap.get("devices"));
|
|
102346
102348
|
if (rows.length > 0) {
|
|
102347
102349
|
tables.devices = rows.map((r) => pickMetricRow(r, ["deviceType"]));
|
|
102348
102350
|
}
|
|
102349
102351
|
}
|
|
102350
|
-
|
|
102352
|
+
{
|
|
102351
102353
|
const rows = items(sectionMap.get("geographic"));
|
|
102352
102354
|
if (rows.length > 0) {
|
|
102353
102355
|
tables.geographic = rows.map(
|
|
@@ -102355,7 +102357,7 @@ async function mergeGoogleAnalysisSnapshotIntoReport(payload, snapshotDir) {
|
|
|
102355
102357
|
);
|
|
102356
102358
|
}
|
|
102357
102359
|
}
|
|
102358
|
-
|
|
102360
|
+
{
|
|
102359
102361
|
const rows = items(sectionMap.get("keywords"));
|
|
102360
102362
|
if (rows.length > 0) {
|
|
102361
102363
|
tables.keywords = rows.map(
|
|
@@ -103814,7 +103816,7 @@ function resolveSectionList(sections, exclude) {
|
|
|
103814
103816
|
const base = include.length > 0 ? SECTIONS.filter((s) => include.includes(s.name)) : SECTIONS;
|
|
103815
103817
|
return base.filter((s) => !drop.has(s.name));
|
|
103816
103818
|
}
|
|
103817
|
-
async function
|
|
103819
|
+
async function runWithConcurrency2(tasks, concurrency) {
|
|
103818
103820
|
const results = new Array(tasks.length);
|
|
103819
103821
|
let cursor = 0;
|
|
103820
103822
|
const workers = new Array(Math.max(1, Math.min(concurrency, tasks.length))).fill(0).map(async () => {
|
|
@@ -103897,7 +103899,7 @@ async function runAllSections(opts) {
|
|
|
103897
103899
|
const cliVersion = getCurrentVersion2();
|
|
103898
103900
|
const overallT0 = performance4.now();
|
|
103899
103901
|
const tasks = targets.map((def) => () => fetchAndWriteSection(def, opts, config, id, cliVersion));
|
|
103900
|
-
let results = await
|
|
103902
|
+
let results = await runWithConcurrency2(tasks, concurrency);
|
|
103901
103903
|
const failedIndices = results.map((r, i) => r.ok ? -1 : i).filter((i) => i >= 0);
|
|
103902
103904
|
if (failedIndices.length > 0) {
|
|
103903
103905
|
console.error(`
|
|
@@ -107055,6 +107057,747 @@ function register9(program2) {
|
|
|
107055
107057
|
// src/index.ts
|
|
107056
107058
|
init_accounts_digest2();
|
|
107057
107059
|
|
|
107060
|
+
// src/commands/guard/budget-circuit.ts
|
|
107061
|
+
init_cli_json_snapshot();
|
|
107062
|
+
|
|
107063
|
+
// src/commands/guard/shared.ts
|
|
107064
|
+
init_auth();
|
|
107065
|
+
init_list_fetch();
|
|
107066
|
+
|
|
107067
|
+
// src/commands/ad/_shared.ts
|
|
107068
|
+
init_auth();
|
|
107069
|
+
async function findItemInList(listUrl, config, id, verbose) {
|
|
107070
|
+
const res = await apiFetch2(listUrl, config, {}, verbose);
|
|
107071
|
+
const item = (res.data ?? []).find((x) => x["id"] === id);
|
|
107072
|
+
if (!item) {
|
|
107073
|
+
throw new Error(`\u672A\u5728\u5217\u8868\u4E2D\u627E\u5230 id="${id}" \u7684\u6761\u76EE`);
|
|
107074
|
+
}
|
|
107075
|
+
return item;
|
|
107076
|
+
}
|
|
107077
|
+
function toGoogleDate(dateStr, offsetDays) {
|
|
107078
|
+
if (dateStr) return dateStr.replace(/-/g, "/");
|
|
107079
|
+
const d = /* @__PURE__ */ new Date();
|
|
107080
|
+
d.setDate(d.getDate() + offsetDays);
|
|
107081
|
+
return d.toISOString().slice(0, 10).replace(/-/g, "/");
|
|
107082
|
+
}
|
|
107083
|
+
function toDisplayMoney(raw) {
|
|
107084
|
+
if (raw == null) return null;
|
|
107085
|
+
const n = Number(raw);
|
|
107086
|
+
if (!Number.isFinite(n)) return null;
|
|
107087
|
+
return n / 100;
|
|
107088
|
+
}
|
|
107089
|
+
function toCentAmount(displayValue) {
|
|
107090
|
+
if (!Number.isFinite(displayValue)) {
|
|
107091
|
+
throw new Error(`toCentAmount: \u8F93\u5165\u5FC5\u987B\u662F\u6709\u9650\u6570\u5B57\uFF0C\u6536\u5230 ${displayValue}`);
|
|
107092
|
+
}
|
|
107093
|
+
return Math.round(displayValue * 100);
|
|
107094
|
+
}
|
|
107095
|
+
var CAMPAIGN_BATCH_MONEY_KEYS = /* @__PURE__ */ new Set([
|
|
107096
|
+
"Budget",
|
|
107097
|
+
"MaxCPCAmount",
|
|
107098
|
+
"MaxCpmAmount",
|
|
107099
|
+
"MaxCPVAmount",
|
|
107100
|
+
"TargetCpa_BidingAmount",
|
|
107101
|
+
"TargetSpend_BidCeilingAmount",
|
|
107102
|
+
"TargetCpaAmount",
|
|
107103
|
+
"MaxCPC"
|
|
107104
|
+
]);
|
|
107105
|
+
function convertBatchMoneyTreeFromYuan(value) {
|
|
107106
|
+
return convertBatchMoneyTreeFromYuanInner(value);
|
|
107107
|
+
}
|
|
107108
|
+
function convertBatchMoneyTreeFromYuanInner(value) {
|
|
107109
|
+
if (Array.isArray(value)) {
|
|
107110
|
+
return value.map(convertBatchMoneyTreeFromYuanInner);
|
|
107111
|
+
}
|
|
107112
|
+
if (value && typeof value === "object") {
|
|
107113
|
+
const out = {};
|
|
107114
|
+
for (const [key, val] of Object.entries(value)) {
|
|
107115
|
+
if (typeof val === "number" && CAMPAIGN_BATCH_MONEY_KEYS.has(key)) {
|
|
107116
|
+
out[key] = toCentAmount(val);
|
|
107117
|
+
} else {
|
|
107118
|
+
out[key] = convertBatchMoneyTreeFromYuanInner(val);
|
|
107119
|
+
}
|
|
107120
|
+
}
|
|
107121
|
+
return out;
|
|
107122
|
+
}
|
|
107123
|
+
return value;
|
|
107124
|
+
}
|
|
107125
|
+
function adgroupListUrl(googleApiUrl, account, startDate, endDate) {
|
|
107126
|
+
const params = new URLSearchParams();
|
|
107127
|
+
params.set("startDate", toGoogleDate(startDate, -30));
|
|
107128
|
+
params.set("endDate", toGoogleDate(endDate, 0));
|
|
107129
|
+
return `${googleApiUrl}/adgroupnmanagement/v2/list/${account}?${params}`;
|
|
107130
|
+
}
|
|
107131
|
+
function requireGoogleApi(config) {
|
|
107132
|
+
if (!config.googleApiUrl) {
|
|
107133
|
+
console.error(
|
|
107134
|
+
"\n\u274C \u672A\u627E\u5230 Google API \u5730\u5740\u3002\n Google API \u5730\u5740\u4ECE TSO API \u5730\u5740\u81EA\u52A8\u63A8\u5BFC\uFF0C\u4E5F\u53EF\u901A\u8FC7\u73AF\u5883\u53D8\u91CF SILUZAN_GOOGLE_API \u8986\u76D6\u3002\n"
|
|
107135
|
+
);
|
|
107136
|
+
process.exit(1);
|
|
107137
|
+
}
|
|
107138
|
+
return config.googleApiUrl;
|
|
107139
|
+
}
|
|
107140
|
+
|
|
107141
|
+
// src/commands/guard/shared.ts
|
|
107142
|
+
init_cli_table();
|
|
107143
|
+
var GUARD_ACCOUNT_CONCURRENCY = 4;
|
|
107144
|
+
async function resolveGuardConfig(opts) {
|
|
107145
|
+
let config = loadConfig(opts.token);
|
|
107146
|
+
if (opts.refreshDp) {
|
|
107147
|
+
config = await refreshDataPermission(config);
|
|
107148
|
+
}
|
|
107149
|
+
return config;
|
|
107150
|
+
}
|
|
107151
|
+
function assertGoogleOnly(media) {
|
|
107152
|
+
if (media !== "Google") {
|
|
107153
|
+
console.error(
|
|
107154
|
+
`
|
|
107155
|
+
\u274C guard \u7194\u65AD\u5199\u76EE\u524D\u4EC5\u652F\u6301 Google\uFF08\u5F53\u524D\uFF1A${media}\uFF09\u3002
|
|
107156
|
+
\u975E Google \u5A92\u4F53\u672C CLI \u65E0\u7CFB\u5217/\u7EC4/\u521B\u610F\u6682\u505C\u5199\u80FD\u529B\uFF1B\u8BF7\u6539\u7528 -m Google\u3002
|
|
107157
|
+
`
|
|
107158
|
+
);
|
|
107159
|
+
process.exit(1);
|
|
107160
|
+
}
|
|
107161
|
+
}
|
|
107162
|
+
async function resolveGuardAccounts(opts, config) {
|
|
107163
|
+
const filterIds = (opts.accounts ?? "").split(",").map((s) => s.trim()).filter(Boolean);
|
|
107164
|
+
if (filterIds.length > 0) {
|
|
107165
|
+
process.stderr.write(
|
|
107166
|
+
`\u23F3 [guard] \u5B50\u96C6\u6A21\u5F0F\uFF1A\u5BF9 -a \u6307\u5B9A\u7684 ${filterIds.length} \u4E2A\u8D26\u6237\u626B\u63CF\u3002
|
|
107167
|
+
`
|
|
107168
|
+
);
|
|
107169
|
+
return filterIds.map((id) => ({ mediaCustomerId: id, name: null }));
|
|
107170
|
+
}
|
|
107171
|
+
const pageSize = Math.max(1, Math.min(500, opts.pageSize ?? 200));
|
|
107172
|
+
const maxPages = Math.max(1, Math.min(200, opts.maxPages ?? 20));
|
|
107173
|
+
const res = await fetchAccountsByMedia("Google", config, {
|
|
107174
|
+
pageSize,
|
|
107175
|
+
maxPages,
|
|
107176
|
+
verbose: opts.verbose
|
|
107177
|
+
});
|
|
107178
|
+
return res.items.map((item) => ({
|
|
107179
|
+
mediaCustomerId: String(item.ma?.mediaCustomerId ?? ""),
|
|
107180
|
+
name: item.ma?.mediaCustomerName ?? null
|
|
107181
|
+
})).filter((a) => a.mediaCustomerId);
|
|
107182
|
+
}
|
|
107183
|
+
async function runWithConcurrency(tasks, concurrency) {
|
|
107184
|
+
const results = new Array(tasks.length);
|
|
107185
|
+
let cursor = 0;
|
|
107186
|
+
const workers = new Array(Math.max(1, Math.min(concurrency, tasks.length || 1))).fill(0).map(async () => {
|
|
107187
|
+
while (true) {
|
|
107188
|
+
const i = cursor++;
|
|
107189
|
+
if (i >= tasks.length) return;
|
|
107190
|
+
results[i] = await tasks[i]();
|
|
107191
|
+
}
|
|
107192
|
+
});
|
|
107193
|
+
await Promise.all(workers);
|
|
107194
|
+
return results;
|
|
107195
|
+
}
|
|
107196
|
+
async function fetchCampaignsForDay(config, accountId, date, verbose) {
|
|
107197
|
+
const googleApiUrl = requireGoogleApi(config);
|
|
107198
|
+
const params = new URLSearchParams();
|
|
107199
|
+
params.set("startDate", toGoogleDate(date, 0));
|
|
107200
|
+
params.set("endDate", toGoogleDate(date, 0));
|
|
107201
|
+
const url = `${googleApiUrl}/campaignmanagement/v2/list/${accountId}?${params}`;
|
|
107202
|
+
const data = await apiFetch2(url, config, {}, verbose);
|
|
107203
|
+
return (data.data ?? []).map((item) => ({
|
|
107204
|
+
id: String(item["id"] ?? ""),
|
|
107205
|
+
name: String(item["name"] ?? ""),
|
|
107206
|
+
statusV2: String(item["statusV2"] ?? ""),
|
|
107207
|
+
budget: toDisplayMoney(item["budget"]),
|
|
107208
|
+
spend: item["spend"] != null && Number.isFinite(Number(item["spend"])) ? Number(item["spend"]) : 0,
|
|
107209
|
+
conversions: item["conversions"] != null && Number.isFinite(Number(item["conversions"])) ? Number(item["conversions"]) : null
|
|
107210
|
+
})).filter((r) => r.id);
|
|
107211
|
+
}
|
|
107212
|
+
async function fetchAdGroupsForDay(config, accountId, date, verbose) {
|
|
107213
|
+
const googleApiUrl = requireGoogleApi(config);
|
|
107214
|
+
const params = new URLSearchParams();
|
|
107215
|
+
params.set("startDate", toGoogleDate(date, 0));
|
|
107216
|
+
params.set("endDate", toGoogleDate(date, 0));
|
|
107217
|
+
const url = `${googleApiUrl}/adgroupnmanagement/v2/list/${accountId}?${params}`;
|
|
107218
|
+
const data = await apiFetch2(url, config, {}, verbose);
|
|
107219
|
+
return (data.data ?? []).map((item) => ({
|
|
107220
|
+
id: String(item["id"] ?? ""),
|
|
107221
|
+
name: String(item["name"] ?? ""),
|
|
107222
|
+
campaignName: String(item["campaignName"] ?? ""),
|
|
107223
|
+
statusV2: String(item["statusV2"] ?? ""),
|
|
107224
|
+
spend: item["spend"] != null && Number.isFinite(Number(item["spend"])) ? Number(item["spend"]) : 0,
|
|
107225
|
+
conversions: item["conversions"] != null && Number.isFinite(Number(item["conversions"])) ? Number(item["conversions"]) : null,
|
|
107226
|
+
targetCpaAmountYuan: toDisplayMoney(item["targetCpaAmount"])
|
|
107227
|
+
})).filter((r) => r.id);
|
|
107228
|
+
}
|
|
107229
|
+
async function fetchAdsForDay(config, accountId, date, verbose) {
|
|
107230
|
+
const googleApiUrl = requireGoogleApi(config);
|
|
107231
|
+
const params = new URLSearchParams();
|
|
107232
|
+
params.set("startDate", toGoogleDate(date, 0));
|
|
107233
|
+
params.set("endDate", toGoogleDate(date, 0));
|
|
107234
|
+
const url = `${googleApiUrl}/admanagement/v2/list/${accountId}?${params}`;
|
|
107235
|
+
const data = await apiFetch2(url, config, {}, verbose);
|
|
107236
|
+
return (data.data ?? []).map((item) => {
|
|
107237
|
+
const rawTitle = item["headlines"]?.[0] ?? item["adTitle"] ?? item["headlinePart1"] ?? item["name"] ?? "";
|
|
107238
|
+
return {
|
|
107239
|
+
id: String(item["id"] ?? ""),
|
|
107240
|
+
name: String(rawTitle),
|
|
107241
|
+
campaign: String(item["campaign"] ?? ""),
|
|
107242
|
+
adGroup: String(item["adGroup"] ?? ""),
|
|
107243
|
+
statusV2: String(item["statusV2"] ?? ""),
|
|
107244
|
+
spend: item["spend"] != null && Number.isFinite(Number(item["spend"])) ? Number(item["spend"]) : 0,
|
|
107245
|
+
conversions: item["conversions"] != null && Number.isFinite(Number(item["conversions"])) ? Number(item["conversions"]) : null
|
|
107246
|
+
};
|
|
107247
|
+
}).filter((r) => r.id);
|
|
107248
|
+
}
|
|
107249
|
+
async function pauseCampaign(config, accountId, campaignId, verbose) {
|
|
107250
|
+
const googleApiUrl = requireGoogleApi(config);
|
|
107251
|
+
const detailUrl = `${googleApiUrl}/campaignmanagement/campaign/${accountId}/${campaignId}`;
|
|
107252
|
+
const res = await apiFetch2(detailUrl, config, {}, verbose);
|
|
107253
|
+
const campaign = res.data ?? res;
|
|
107254
|
+
const body = { ...campaign, statusV2: "Paused" };
|
|
107255
|
+
await apiFetch2(detailUrl, config, { method: "PUT", body: JSON.stringify(body) }, verbose);
|
|
107256
|
+
}
|
|
107257
|
+
async function pauseAdGroup(config, accountId, adGroupId, date, verbose) {
|
|
107258
|
+
const googleApiUrl = requireGoogleApi(config);
|
|
107259
|
+
const listUrl = adgroupListUrl(googleApiUrl, accountId, date, date);
|
|
107260
|
+
const adgroup = await findItemInList(
|
|
107261
|
+
listUrl,
|
|
107262
|
+
config,
|
|
107263
|
+
adGroupId,
|
|
107264
|
+
verbose
|
|
107265
|
+
);
|
|
107266
|
+
const putUrl = `${googleApiUrl}/adgroupnmanagement/adgroup/${accountId}/${adGroupId}`;
|
|
107267
|
+
await apiFetch2(
|
|
107268
|
+
putUrl,
|
|
107269
|
+
config,
|
|
107270
|
+
{ method: "PUT", body: JSON.stringify({ ...adgroup, statusV2: "Paused" }) },
|
|
107271
|
+
verbose
|
|
107272
|
+
);
|
|
107273
|
+
}
|
|
107274
|
+
async function pauseAd(config, accountId, adId, date, verbose) {
|
|
107275
|
+
const googleApiUrl = requireGoogleApi(config);
|
|
107276
|
+
const params = new URLSearchParams();
|
|
107277
|
+
params.set("startDate", toGoogleDate(date, 0));
|
|
107278
|
+
params.set("endDate", toGoogleDate(date, 0));
|
|
107279
|
+
const listUrl = `${googleApiUrl}/admanagement/v2/list/${accountId}?${params}`;
|
|
107280
|
+
const ad = await findItemInList(listUrl, config, adId, verbose);
|
|
107281
|
+
const putUrl = `${googleApiUrl}/admanagement/campaign/${accountId}/${adId}`;
|
|
107282
|
+
await apiFetch2(
|
|
107283
|
+
putUrl,
|
|
107284
|
+
config,
|
|
107285
|
+
{ method: "PUT", body: JSON.stringify({ ...ad, statusV2: "Paused" }) },
|
|
107286
|
+
verbose
|
|
107287
|
+
);
|
|
107288
|
+
}
|
|
107289
|
+
function printHitsTable(title, rows, cols) {
|
|
107290
|
+
console.log(`
|
|
107291
|
+
${title}
|
|
107292
|
+
`);
|
|
107293
|
+
if (rows.length === 0) {
|
|
107294
|
+
console.log(" \uFF08\u65E0\u547D\u4E2D\uFF09\n");
|
|
107295
|
+
return;
|
|
107296
|
+
}
|
|
107297
|
+
printCliTable(rows, cols);
|
|
107298
|
+
console.log();
|
|
107299
|
+
}
|
|
107300
|
+
|
|
107301
|
+
// src/commands/guard/rules.ts
|
|
107302
|
+
function moneyGte(leftYuan, rightYuan) {
|
|
107303
|
+
return Math.round(leftYuan * 100) >= Math.round(rightYuan * 100);
|
|
107304
|
+
}
|
|
107305
|
+
function isBudgetCircuitHit(spend, budgetYuan, ratio) {
|
|
107306
|
+
if (spend == null || budgetYuan == null) return false;
|
|
107307
|
+
if (!Number.isFinite(spend) || !Number.isFinite(budgetYuan) || !Number.isFinite(ratio)) {
|
|
107308
|
+
return false;
|
|
107309
|
+
}
|
|
107310
|
+
if (budgetYuan <= 0 || ratio <= 0) return false;
|
|
107311
|
+
return moneyGte(spend, budgetYuan * ratio);
|
|
107312
|
+
}
|
|
107313
|
+
function isZeroConvHit(spend, conversions, targetCpaYuan, multiple) {
|
|
107314
|
+
if (spend == null || targetCpaYuan == null) return false;
|
|
107315
|
+
if (!Number.isFinite(spend) || !Number.isFinite(targetCpaYuan) || !Number.isFinite(multiple)) {
|
|
107316
|
+
return false;
|
|
107317
|
+
}
|
|
107318
|
+
if (targetCpaYuan <= 0 || multiple <= 0) return false;
|
|
107319
|
+
const conv = conversions == null || !Number.isFinite(conversions) ? 0 : conversions;
|
|
107320
|
+
if (conv !== 0) return false;
|
|
107321
|
+
return moneyGte(spend, targetCpaYuan * multiple);
|
|
107322
|
+
}
|
|
107323
|
+
function shanghaiToday(now = /* @__PURE__ */ new Date()) {
|
|
107324
|
+
return new Intl.DateTimeFormat("en-CA", {
|
|
107325
|
+
timeZone: "Asia/Shanghai",
|
|
107326
|
+
year: "numeric",
|
|
107327
|
+
month: "2-digit",
|
|
107328
|
+
day: "2-digit"
|
|
107329
|
+
}).format(now);
|
|
107330
|
+
}
|
|
107331
|
+
function isEnabledStatus(statusV2) {
|
|
107332
|
+
return String(statusV2 ?? "").toLowerCase() === "enabled";
|
|
107333
|
+
}
|
|
107334
|
+
|
|
107335
|
+
// src/commands/guard/budget-circuit.ts
|
|
107336
|
+
async function runBudgetCircuit(opts) {
|
|
107337
|
+
assertGoogleOnly(opts.media);
|
|
107338
|
+
const config = await resolveGuardConfig(opts);
|
|
107339
|
+
const date = opts.date?.trim() || shanghaiToday();
|
|
107340
|
+
const ratio = opts.ratio ?? 1.1;
|
|
107341
|
+
if (!Number.isFinite(ratio) || ratio <= 0) {
|
|
107342
|
+
console.error("\n\u274C --ratio \u5FC5\u987B\u4E3A\u6B63\u6570\uFF08\u4F8B\u5982 1.1\uFF09\n");
|
|
107343
|
+
process.exit(1);
|
|
107344
|
+
}
|
|
107345
|
+
const statusFilter = opts.status === void 0 ? "Enabled" : opts.status;
|
|
107346
|
+
const filterAll = statusFilter === "" || statusFilter === "*";
|
|
107347
|
+
const apply = Boolean(opts.apply);
|
|
107348
|
+
const accounts = await resolveGuardAccounts(opts, config);
|
|
107349
|
+
if (accounts.length === 0) {
|
|
107350
|
+
console.error("\n\u274C \u672A\u627E\u5230\u53EF\u626B\u63CF\u7684 Google \u8D26\u6237\u3002\n");
|
|
107351
|
+
process.exit(1);
|
|
107352
|
+
}
|
|
107353
|
+
process.stderr.write(
|
|
107354
|
+
`\u23F3 [guard budget-circuit] ${accounts.length} \u6237 \xB7 \u65E5\u671F ${date} \xB7 ratio=${ratio}${apply ? " \xB7 APPLY" : " \xB7 dry-run"}
|
|
107355
|
+
`
|
|
107356
|
+
);
|
|
107357
|
+
const accountResults = await runWithConcurrency(
|
|
107358
|
+
accounts.map((acc) => async () => {
|
|
107359
|
+
try {
|
|
107360
|
+
const campaigns = await fetchCampaignsForDay(
|
|
107361
|
+
config,
|
|
107362
|
+
acc.mediaCustomerId,
|
|
107363
|
+
date,
|
|
107364
|
+
opts.verbose
|
|
107365
|
+
);
|
|
107366
|
+
const hits2 = [];
|
|
107367
|
+
let checked = 0;
|
|
107368
|
+
for (const c of campaigns) {
|
|
107369
|
+
if (!filterAll) {
|
|
107370
|
+
if (statusFilter.toLowerCase() === "enabled") {
|
|
107371
|
+
if (!isEnabledStatus(c.statusV2)) continue;
|
|
107372
|
+
} else if (String(c.statusV2) !== statusFilter) {
|
|
107373
|
+
continue;
|
|
107374
|
+
}
|
|
107375
|
+
}
|
|
107376
|
+
checked++;
|
|
107377
|
+
if (!isBudgetCircuitHit(c.spend, c.budget, ratio)) continue;
|
|
107378
|
+
const budget = c.budget;
|
|
107379
|
+
const hit = {
|
|
107380
|
+
mediaCustomerId: acc.mediaCustomerId,
|
|
107381
|
+
accountName: acc.name,
|
|
107382
|
+
campaignId: c.id,
|
|
107383
|
+
campaignName: c.name,
|
|
107384
|
+
statusV2: c.statusV2,
|
|
107385
|
+
spend: c.spend,
|
|
107386
|
+
budget,
|
|
107387
|
+
ratio,
|
|
107388
|
+
threshold: budget * ratio,
|
|
107389
|
+
applied: false
|
|
107390
|
+
};
|
|
107391
|
+
if (apply) {
|
|
107392
|
+
try {
|
|
107393
|
+
await pauseCampaign(config, acc.mediaCustomerId, c.id, opts.verbose);
|
|
107394
|
+
hit.applied = true;
|
|
107395
|
+
hit.statusV2 = "Paused";
|
|
107396
|
+
} catch (err) {
|
|
107397
|
+
hit.applyError = err instanceof Error ? err.message : String(err);
|
|
107398
|
+
}
|
|
107399
|
+
}
|
|
107400
|
+
hits2.push(hit);
|
|
107401
|
+
}
|
|
107402
|
+
return {
|
|
107403
|
+
mediaCustomerId: acc.mediaCustomerId,
|
|
107404
|
+
accountName: acc.name,
|
|
107405
|
+
checked,
|
|
107406
|
+
hits: hits2
|
|
107407
|
+
};
|
|
107408
|
+
} catch (err) {
|
|
107409
|
+
return {
|
|
107410
|
+
mediaCustomerId: acc.mediaCustomerId,
|
|
107411
|
+
accountName: acc.name,
|
|
107412
|
+
checked: 0,
|
|
107413
|
+
hits: [],
|
|
107414
|
+
error: err instanceof Error ? err.message : String(err)
|
|
107415
|
+
};
|
|
107416
|
+
}
|
|
107417
|
+
}),
|
|
107418
|
+
GUARD_ACCOUNT_CONCURRENCY
|
|
107419
|
+
);
|
|
107420
|
+
const hits = accountResults.flatMap((r) => r.hits);
|
|
107421
|
+
const errors = accountResults.filter((r) => r.error).map((r) => ({
|
|
107422
|
+
mediaCustomerId: r.mediaCustomerId,
|
|
107423
|
+
accountName: r.accountName,
|
|
107424
|
+
error: r.error
|
|
107425
|
+
}));
|
|
107426
|
+
const paused = hits.filter((h) => h.applied);
|
|
107427
|
+
const checkedCampaigns = accountResults.reduce((n, r) => n + r.checked, 0);
|
|
107428
|
+
const payload = {
|
|
107429
|
+
meta: {
|
|
107430
|
+
media: "Google",
|
|
107431
|
+
date,
|
|
107432
|
+
ratio,
|
|
107433
|
+
statusFilter: filterAll ? "*" : statusFilter,
|
|
107434
|
+
apply,
|
|
107435
|
+
note: "\u975E Google \u5A92\u4F53\u672C CLI \u65E0\u7CFB\u5217\u7194\u65AD\u5199\u80FD\u529B\uFF1B\u672C\u547D\u4EE4\u4EC5\u626B\u63CF Google\u3002"
|
|
107436
|
+
},
|
|
107437
|
+
summary: {
|
|
107438
|
+
label: "\u8D85\u9884\u7B97\u7194\u65AD",
|
|
107439
|
+
accountCount: accounts.length,
|
|
107440
|
+
checkedCampaigns,
|
|
107441
|
+
hitCount: hits.length,
|
|
107442
|
+
pausedCount: paused.length,
|
|
107443
|
+
errorCount: errors.length,
|
|
107444
|
+
mode: apply ? "apply" : "dry-run"
|
|
107445
|
+
},
|
|
107446
|
+
hits,
|
|
107447
|
+
paused,
|
|
107448
|
+
errors
|
|
107449
|
+
};
|
|
107450
|
+
if (await emitCliJsonOrSnapshot(opts, {
|
|
107451
|
+
section: "guard-budget-circuit",
|
|
107452
|
+
commandLabel: "guard budget-circuit",
|
|
107453
|
+
payload
|
|
107454
|
+
})) {
|
|
107455
|
+
}
|
|
107456
|
+
const title = `\u8D85\u9884\u7B97\u7194\u65AD\uFF08${apply ? "\u5DF2\u5C1D\u8BD5\u6682\u505C" : "dry-run"}\uFF09 \xB7 ${date} \xB7 ratio=${ratio} \xB7 \u547D\u4E2D ${hits.length}/${checkedCampaigns}`;
|
|
107457
|
+
printHitsTable(
|
|
107458
|
+
title,
|
|
107459
|
+
hits.map((h) => ({
|
|
107460
|
+
account: h.mediaCustomerId,
|
|
107461
|
+
campaign: h.campaignName.slice(0, 36),
|
|
107462
|
+
spend: h.spend.toFixed(2),
|
|
107463
|
+
budget: h.budget.toFixed(2),
|
|
107464
|
+
threshold: h.threshold.toFixed(2),
|
|
107465
|
+
applied: h.applied ? "Paused" : h.applyError ? `ERR:${h.applyError.slice(0, 20)}` : "\u2014"
|
|
107466
|
+
})),
|
|
107467
|
+
[
|
|
107468
|
+
{ key: "account", header: "\u8D26\u6237ID" },
|
|
107469
|
+
{ key: "campaign", header: "\u7CFB\u5217" },
|
|
107470
|
+
{ key: "spend", header: "\u5F53\u65E5\u82B1\u8D39" },
|
|
107471
|
+
{ key: "budget", header: "\u65E5\u9884\u7B97" },
|
|
107472
|
+
{ key: "threshold", header: "\u9608\u503C" },
|
|
107473
|
+
{ key: "applied", header: "\u6682\u505C" }
|
|
107474
|
+
]
|
|
107475
|
+
);
|
|
107476
|
+
if (errors.length > 0) {
|
|
107477
|
+
console.error(`\u26A0\uFE0F ${errors.length} \u4E2A\u8D26\u6237\u62C9\u6570\u5931\u8D25\uFF08\u8BE6\u89C1 json-out errors[]\uFF09`);
|
|
107478
|
+
for (const e of errors.slice(0, 5)) {
|
|
107479
|
+
console.error(` \u2022 ${e.mediaCustomerId}: ${e.error}`);
|
|
107480
|
+
}
|
|
107481
|
+
}
|
|
107482
|
+
if (!apply && hits.length > 0) {
|
|
107483
|
+
console.log(
|
|
107484
|
+
'\u63D0\u793A\uFF1A\u4EE5\u4E0A\u4E3A dry-run\u3002\u786E\u8BA4\u540E\u8FFD\u52A0 --apply --commit "budget-circuit <date>" \u6267\u884C\u6682\u505C\u3002\n'
|
|
107485
|
+
);
|
|
107486
|
+
} else if (apply) {
|
|
107487
|
+
console.log(
|
|
107488
|
+
`\u2705 apply \u5B8C\u6210\uFF1A\u547D\u4E2D ${hits.length}\uFF0C\u6210\u529F\u6682\u505C ${paused.length}${hits.length - paused.length > 0 ? `\uFF0C\u5931\u8D25 ${hits.length - paused.length}` : ""}\u3002
|
|
107489
|
+
`
|
|
107490
|
+
);
|
|
107491
|
+
} else {
|
|
107492
|
+
console.log("\u2705 \u4ECA\u65E5\u65E0\u7CFB\u5217\u89E6\u53D1\u8D85\u9884\u7B97\u7194\u65AD\u3002\n");
|
|
107493
|
+
}
|
|
107494
|
+
}
|
|
107495
|
+
|
|
107496
|
+
// src/commands/guard/zero-conv.ts
|
|
107497
|
+
init_cli_json_snapshot();
|
|
107498
|
+
function parseLevel(raw) {
|
|
107499
|
+
const v = (raw ?? "adgroup").toLowerCase();
|
|
107500
|
+
if (v === "adgroup" || v === "ad" || v === "both") return v;
|
|
107501
|
+
console.error("\n\u274C --level \u987B\u4E3A adgroup | ad | both\n");
|
|
107502
|
+
process.exit(1);
|
|
107503
|
+
}
|
|
107504
|
+
async function runZeroConv(opts) {
|
|
107505
|
+
assertGoogleOnly(opts.media);
|
|
107506
|
+
const config = await resolveGuardConfig(opts);
|
|
107507
|
+
const date = opts.date?.trim() || shanghaiToday();
|
|
107508
|
+
const multiple = opts.cpaMultiple ?? 3;
|
|
107509
|
+
if (!Number.isFinite(multiple) || multiple <= 0) {
|
|
107510
|
+
console.error("\n\u274C --cpa-multiple \u5FC5\u987B\u4E3A\u6B63\u6570\uFF08\u4F8B\u5982 3\uFF09\n");
|
|
107511
|
+
process.exit(1);
|
|
107512
|
+
}
|
|
107513
|
+
const level = parseLevel(opts.level);
|
|
107514
|
+
const fallback = opts.fallbackTargetCpa != null && Number.isFinite(opts.fallbackTargetCpa) ? opts.fallbackTargetCpa : null;
|
|
107515
|
+
const onlyEnabled = opts.onlyEnabled !== false;
|
|
107516
|
+
const apply = Boolean(opts.apply);
|
|
107517
|
+
if ((level === "ad" || level === "both") && fallback == null) {
|
|
107518
|
+
process.stderr.write(
|
|
107519
|
+
"\u26A0\uFE0F [guard zero-conv] \u521B\u610F\u7EF4\u65E0\u7EC4\u7EA7 targetCpa \u5B57\u6BB5\uFF0C\u672A\u4F20 --fallback-target-cpa \u65F6\u521B\u610F\u547D\u4E2D\u53EF\u80FD\u4E3A 0\uFF08\u4F1A\u8BB0\u5165 skipped\uFF09\u3002\n"
|
|
107520
|
+
);
|
|
107521
|
+
}
|
|
107522
|
+
const accounts = await resolveGuardAccounts(opts, config);
|
|
107523
|
+
if (accounts.length === 0) {
|
|
107524
|
+
console.error("\n\u274C \u672A\u627E\u5230\u53EF\u626B\u63CF\u7684 Google \u8D26\u6237\u3002\n");
|
|
107525
|
+
process.exit(1);
|
|
107526
|
+
}
|
|
107527
|
+
process.stderr.write(
|
|
107528
|
+
`\u23F3 [guard zero-conv] ${accounts.length} \u6237 \xB7 ${date} \xB7 \xD7${multiple} \xB7 level=${level}${apply ? " \xB7 APPLY" : " \xB7 dry-run"}
|
|
107529
|
+
`
|
|
107530
|
+
);
|
|
107531
|
+
const accountResults = await runWithConcurrency(
|
|
107532
|
+
accounts.map((acc) => async () => {
|
|
107533
|
+
try {
|
|
107534
|
+
const hits2 = [];
|
|
107535
|
+
const skipped2 = [];
|
|
107536
|
+
let checked2 = 0;
|
|
107537
|
+
if (level === "adgroup" || level === "both") {
|
|
107538
|
+
const groups = await fetchAdGroupsForDay(
|
|
107539
|
+
config,
|
|
107540
|
+
acc.mediaCustomerId,
|
|
107541
|
+
date,
|
|
107542
|
+
opts.verbose
|
|
107543
|
+
);
|
|
107544
|
+
for (const g of groups) {
|
|
107545
|
+
if (onlyEnabled && !isEnabledStatus(g.statusV2)) continue;
|
|
107546
|
+
checked2++;
|
|
107547
|
+
const target = g.targetCpaAmountYuan ?? fallback;
|
|
107548
|
+
if (target == null) {
|
|
107549
|
+
skipped2.push({
|
|
107550
|
+
mediaCustomerId: acc.mediaCustomerId,
|
|
107551
|
+
level: "adgroup",
|
|
107552
|
+
objectId: g.id,
|
|
107553
|
+
objectName: g.name,
|
|
107554
|
+
reason: "missing_target_cpa"
|
|
107555
|
+
});
|
|
107556
|
+
continue;
|
|
107557
|
+
}
|
|
107558
|
+
if (!isZeroConvHit(g.spend, g.conversions, target, multiple)) continue;
|
|
107559
|
+
const hit = {
|
|
107560
|
+
mediaCustomerId: acc.mediaCustomerId,
|
|
107561
|
+
accountName: acc.name,
|
|
107562
|
+
level: "adgroup",
|
|
107563
|
+
objectId: g.id,
|
|
107564
|
+
objectName: g.name,
|
|
107565
|
+
parentName: g.campaignName,
|
|
107566
|
+
statusV2: g.statusV2,
|
|
107567
|
+
spend: g.spend,
|
|
107568
|
+
conversions: g.conversions ?? 0,
|
|
107569
|
+
targetCpaYuan: target,
|
|
107570
|
+
multiple,
|
|
107571
|
+
threshold: target * multiple,
|
|
107572
|
+
applied: false
|
|
107573
|
+
};
|
|
107574
|
+
if (apply) {
|
|
107575
|
+
try {
|
|
107576
|
+
await pauseAdGroup(config, acc.mediaCustomerId, g.id, date, opts.verbose);
|
|
107577
|
+
hit.applied = true;
|
|
107578
|
+
hit.statusV2 = "Paused";
|
|
107579
|
+
} catch (err) {
|
|
107580
|
+
hit.applyError = err instanceof Error ? err.message : String(err);
|
|
107581
|
+
}
|
|
107582
|
+
}
|
|
107583
|
+
hits2.push(hit);
|
|
107584
|
+
}
|
|
107585
|
+
}
|
|
107586
|
+
if (level === "ad" || level === "both") {
|
|
107587
|
+
const ads = await fetchAdsForDay(config, acc.mediaCustomerId, date, opts.verbose);
|
|
107588
|
+
for (const ad of ads) {
|
|
107589
|
+
if (onlyEnabled && !isEnabledStatus(ad.statusV2)) continue;
|
|
107590
|
+
checked2++;
|
|
107591
|
+
if (fallback == null) {
|
|
107592
|
+
skipped2.push({
|
|
107593
|
+
mediaCustomerId: acc.mediaCustomerId,
|
|
107594
|
+
level: "ad",
|
|
107595
|
+
objectId: ad.id,
|
|
107596
|
+
objectName: ad.name,
|
|
107597
|
+
reason: "missing_fallback_target_cpa"
|
|
107598
|
+
});
|
|
107599
|
+
continue;
|
|
107600
|
+
}
|
|
107601
|
+
if (!isZeroConvHit(ad.spend, ad.conversions, fallback, multiple)) continue;
|
|
107602
|
+
const hit = {
|
|
107603
|
+
mediaCustomerId: acc.mediaCustomerId,
|
|
107604
|
+
accountName: acc.name,
|
|
107605
|
+
level: "ad",
|
|
107606
|
+
objectId: ad.id,
|
|
107607
|
+
objectName: ad.name,
|
|
107608
|
+
parentName: `${ad.campaign} / ${ad.adGroup}`,
|
|
107609
|
+
statusV2: ad.statusV2,
|
|
107610
|
+
spend: ad.spend,
|
|
107611
|
+
conversions: ad.conversions ?? 0,
|
|
107612
|
+
targetCpaYuan: fallback,
|
|
107613
|
+
multiple,
|
|
107614
|
+
threshold: fallback * multiple,
|
|
107615
|
+
applied: false
|
|
107616
|
+
};
|
|
107617
|
+
if (apply) {
|
|
107618
|
+
try {
|
|
107619
|
+
await pauseAd(config, acc.mediaCustomerId, ad.id, date, opts.verbose);
|
|
107620
|
+
hit.applied = true;
|
|
107621
|
+
hit.statusV2 = "Paused";
|
|
107622
|
+
} catch (err) {
|
|
107623
|
+
hit.applyError = err instanceof Error ? err.message : String(err);
|
|
107624
|
+
}
|
|
107625
|
+
}
|
|
107626
|
+
hits2.push(hit);
|
|
107627
|
+
}
|
|
107628
|
+
}
|
|
107629
|
+
return {
|
|
107630
|
+
mediaCustomerId: acc.mediaCustomerId,
|
|
107631
|
+
accountName: acc.name,
|
|
107632
|
+
checked: checked2,
|
|
107633
|
+
hits: hits2,
|
|
107634
|
+
skipped: skipped2
|
|
107635
|
+
};
|
|
107636
|
+
} catch (err) {
|
|
107637
|
+
return {
|
|
107638
|
+
mediaCustomerId: acc.mediaCustomerId,
|
|
107639
|
+
accountName: acc.name,
|
|
107640
|
+
checked: 0,
|
|
107641
|
+
hits: [],
|
|
107642
|
+
skipped: [],
|
|
107643
|
+
error: err instanceof Error ? err.message : String(err)
|
|
107644
|
+
};
|
|
107645
|
+
}
|
|
107646
|
+
}),
|
|
107647
|
+
GUARD_ACCOUNT_CONCURRENCY
|
|
107648
|
+
);
|
|
107649
|
+
const hits = accountResults.flatMap((r) => r.hits);
|
|
107650
|
+
const skipped = accountResults.flatMap((r) => r.skipped);
|
|
107651
|
+
const errors = accountResults.filter((r) => r.error).map((r) => ({
|
|
107652
|
+
mediaCustomerId: r.mediaCustomerId,
|
|
107653
|
+
accountName: r.accountName,
|
|
107654
|
+
error: r.error
|
|
107655
|
+
}));
|
|
107656
|
+
const paused = hits.filter((h) => h.applied);
|
|
107657
|
+
const checked = accountResults.reduce((n, r) => n + r.checked, 0);
|
|
107658
|
+
const payload = {
|
|
107659
|
+
meta: {
|
|
107660
|
+
media: "Google",
|
|
107661
|
+
date,
|
|
107662
|
+
cpaMultiple: multiple,
|
|
107663
|
+
level,
|
|
107664
|
+
fallbackTargetCpa: fallback,
|
|
107665
|
+
apply,
|
|
107666
|
+
note: "\u975E Google \u5A92\u4F53\u672C CLI \u65E0\u7EC4/\u521B\u610F\u7194\u65AD\u5199\u80FD\u529B\uFF1B\u672C\u547D\u4EE4\u4EC5\u626B\u63CF Google\u3002P1\u300C\u7A7A\u8017\u7194\u65AD\u300D\u901A\u77E5\u7531\u5BBF\u4E3B\u53D1\u9001\u3002"
|
|
107667
|
+
},
|
|
107668
|
+
summary: {
|
|
107669
|
+
label: "\u7A7A\u8017\u7194\u65AD",
|
|
107670
|
+
accountCount: accounts.length,
|
|
107671
|
+
checkedObjects: checked,
|
|
107672
|
+
hitCount: hits.length,
|
|
107673
|
+
pausedCount: paused.length,
|
|
107674
|
+
skippedCount: skipped.length,
|
|
107675
|
+
errorCount: errors.length,
|
|
107676
|
+
mode: apply ? "apply" : "dry-run"
|
|
107677
|
+
},
|
|
107678
|
+
hits,
|
|
107679
|
+
paused,
|
|
107680
|
+
skipped,
|
|
107681
|
+
errors
|
|
107682
|
+
};
|
|
107683
|
+
await emitCliJsonOrSnapshot(opts, {
|
|
107684
|
+
section: "guard-zero-conv",
|
|
107685
|
+
commandLabel: "guard zero-conv",
|
|
107686
|
+
payload
|
|
107687
|
+
});
|
|
107688
|
+
const title = `\u7A7A\u8017\u7194\u65AD\uFF08${apply ? "\u5DF2\u5C1D\u8BD5\u6682\u505C" : "dry-run"}\uFF09 \xB7 ${date} \xB7 \xD7${multiple} \xB7 \u547D\u4E2D ${hits.length}/${checked}`;
|
|
107689
|
+
printHitsTable(
|
|
107690
|
+
title,
|
|
107691
|
+
hits.map((h) => ({
|
|
107692
|
+
account: h.mediaCustomerId,
|
|
107693
|
+
level: h.level,
|
|
107694
|
+
name: h.objectName.slice(0, 32),
|
|
107695
|
+
spend: h.spend.toFixed(2),
|
|
107696
|
+
target: h.targetCpaYuan.toFixed(2),
|
|
107697
|
+
threshold: h.threshold.toFixed(2),
|
|
107698
|
+
applied: h.applied ? "Paused" : h.applyError ? `ERR` : "\u2014"
|
|
107699
|
+
})),
|
|
107700
|
+
[
|
|
107701
|
+
{ key: "account", header: "\u8D26\u6237ID" },
|
|
107702
|
+
{ key: "level", header: "\u5C42\u7EA7" },
|
|
107703
|
+
{ key: "name", header: "\u5BF9\u8C61" },
|
|
107704
|
+
{ key: "spend", header: "\u5F53\u65E5\u82B1\u8D39" },
|
|
107705
|
+
{ key: "target", header: "\u76EE\u6807CPA" },
|
|
107706
|
+
{ key: "threshold", header: "\u9608\u503C" },
|
|
107707
|
+
{ key: "applied", header: "\u6682\u505C" }
|
|
107708
|
+
]
|
|
107709
|
+
);
|
|
107710
|
+
if (errors.length > 0) {
|
|
107711
|
+
console.error(`\u26A0\uFE0F ${errors.length} \u4E2A\u8D26\u6237\u62C9\u6570\u5931\u8D25\uFF08\u8BE6\u89C1 json-out errors[]\uFF09`);
|
|
107712
|
+
}
|
|
107713
|
+
if (!apply && hits.length > 0) {
|
|
107714
|
+
console.log(
|
|
107715
|
+
'\u63D0\u793A\uFF1A\u4EE5\u4E0A\u4E3A dry-run\u3002\u786E\u8BA4\u540E\u8FFD\u52A0 --apply --commit "zero-conv <date>" \u6267\u884C\u6682\u505C\u3002\n'
|
|
107716
|
+
);
|
|
107717
|
+
} else if (apply) {
|
|
107718
|
+
console.log(
|
|
107719
|
+
`\u2705 apply \u5B8C\u6210\uFF1A\u547D\u4E2D ${hits.length}\uFF0C\u6210\u529F\u6682\u505C ${paused.length}${hits.length - paused.length > 0 ? `\uFF0C\u5931\u8D25 ${hits.length - paused.length}` : ""}\u3002
|
|
107720
|
+
`
|
|
107721
|
+
);
|
|
107722
|
+
} else {
|
|
107723
|
+
console.log("\u2705 \u4ECA\u65E5\u65E0\u5BF9\u8C61\u89E6\u53D1\u7A7A\u8017\u7194\u65AD\u3002\n");
|
|
107724
|
+
}
|
|
107725
|
+
}
|
|
107726
|
+
|
|
107727
|
+
// src/commands/guard/_register.ts
|
|
107728
|
+
function register11(program2) {
|
|
107729
|
+
const guard = program2.command("guard").description(
|
|
107730
|
+
"Google \u6295\u653E\u81EA\u63A7\u7194\u65AD\uFF1A\u5168\u6237\uFF08\u6216 -a \u5B50\u96C6\uFF09\u626B\u63CF\u5F53\u65E5\u8D85\u9884\u7B97/\u7A7A\u8017\u547D\u4E2D\uFF0C\u9ED8\u8BA4 dry-run\uFF1B--apply \u65F6\u6682\u505C\u547D\u4E2D\u5BF9\u8C61\uFF08\u987B\u5168\u5C40 --commit\uFF09\u3002\u975E Google \u5A92\u4F53\u4E0D\u652F\u6301\u3002"
|
|
107731
|
+
);
|
|
107732
|
+
guard.command("budget-circuit").description(
|
|
107733
|
+
"\u5355\u65E5\u9884\u7B97\u7194\u65AD\uFF1A\u5F53\u65E5\u7CFB\u5217 spend \u2265 \u65E5\u9884\u7B97 \xD7 ratio \u65F6\u5217\u51FA\uFF08\u53EF\u9009\u6682\u505C\uFF09\u3002\u66FF\u4EE3\u300Clist-accounts \xD7 N + \u9010\u6237 ad campaigns + \u811A\u672C\u300D\u3002"
|
|
107734
|
+
).requiredOption("-m, --media <type>", "\u76EE\u524D\u4EC5\u652F\u6301 Google").option(
|
|
107735
|
+
"-a, --accounts <ids>",
|
|
107736
|
+
"\u6307\u5B9A\u8D26\u6237 mediaCustomerId\uFF0C\u9017\u53F7\u5206\u9694\uFF1B\u7559\u7A7A\u5219\u626B\u63CF\u540D\u4E0B\u5168\u90E8 Google \u8D26\u6237"
|
|
107737
|
+
).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(
|
|
107738
|
+
"--status <statusV2>",
|
|
107739
|
+
"\u53EA\u68C0\u67E5\u8BE5\u72B6\u6001\u7684\u7CFB\u5217\uFF08\u9ED8\u8BA4 Enabled\uFF1B\u4F20 * \u8868\u793A\u4E0D\u8FC7\u6EE4\uFF09",
|
|
107740
|
+
"Enabled"
|
|
107741
|
+
).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(
|
|
107742
|
+
"--json-out <path>",
|
|
107743
|
+
"\u843D\u76D8\u76EE\u5F55\u6216 *.json\uFF1Bstdout \u4E00\u884C\u6458\u8981\uFF1B\u4EBA\u7C7B\u53EF\u8BFB\u8868\u59CB\u7EC8\u6253\u5370",
|
|
107744
|
+
void 0
|
|
107745
|
+
).option("--refresh-dp", "\u5F3A\u5236\u91CD\u62C9 Datapermission", false).option("--verbose", "\u8BE6\u7EC6\u9519\u8BEF", false).action(
|
|
107746
|
+
async (opts) => {
|
|
107747
|
+
await runBudgetCircuit({
|
|
107748
|
+
media: opts.media,
|
|
107749
|
+
accounts: opts.accounts,
|
|
107750
|
+
date: opts.date,
|
|
107751
|
+
ratio: opts.ratio,
|
|
107752
|
+
status: opts.status,
|
|
107753
|
+
apply: opts.apply,
|
|
107754
|
+
pageSize: opts.pageSize,
|
|
107755
|
+
maxPages: opts.maxPages,
|
|
107756
|
+
token: opts.token,
|
|
107757
|
+
jsonOut: opts.jsonOut,
|
|
107758
|
+
refreshDp: opts.refreshDp,
|
|
107759
|
+
verbose: opts.verbose
|
|
107760
|
+
});
|
|
107761
|
+
}
|
|
107762
|
+
);
|
|
107763
|
+
guard.command("zero-conv").description(
|
|
107764
|
+
"\u7A7A\u8017\u7194\u65AD\uFF1A\u5F53\u65E5 spend \u2265 \u76EE\u6807 CPA \xD7 N \u4E14\u8F6C\u5316\u4E3A 0 \u65F6\u5217\u51FA\u7EC4/\u521B\u610F\uFF08\u53EF\u9009\u6682\u505C\uFF09\u3002\u7ED3\u679C summary.label=\u300C\u7A7A\u8017\u7194\u65AD\u300D\uFF1BP1 \u901A\u77E5\u7531\u5BBF\u4E3B\u53D1\u9001\u3002"
|
|
107765
|
+
).requiredOption("-m, --media <type>", "\u76EE\u524D\u4EC5\u652F\u6301 Google").option(
|
|
107766
|
+
"-a, --accounts <ids>",
|
|
107767
|
+
"\u6307\u5B9A\u8D26\u6237 mediaCustomerId\uFF0C\u9017\u53F7\u5206\u9694\uFF1B\u7559\u7A7A\u5219\u626B\u63CF\u540D\u4E0B\u5168\u90E8 Google \u8D26\u6237"
|
|
107768
|
+
).option("--date <YYYY-MM-DD>", "\u7EDF\u8BA1\u65E5\uFF08Asia/Shanghai \u9ED8\u8BA4\u4ECA\u5929\uFF09").option(
|
|
107769
|
+
"--cpa-multiple <n>",
|
|
107770
|
+
"\u500D\u6570 N\uFF1Aspend >= targetCpa \xD7 N\uFF08\u9ED8\u8BA4 3\uFF09",
|
|
107771
|
+
(v) => parseFloat(v)
|
|
107772
|
+
).option(
|
|
107773
|
+
"--level <level>",
|
|
107774
|
+
"\u626B\u63CF\u5C42\u7EA7\uFF1Aadgroup | ad | both\uFF08\u9ED8\u8BA4 adgroup\uFF09",
|
|
107775
|
+
"adgroup"
|
|
107776
|
+
).option(
|
|
107777
|
+
"--fallback-target-cpa <yuan>",
|
|
107778
|
+
"\u7EC4\u65E0 targetCpa\uFF08\u6216\u521B\u610F\u7EF4\uFF09\u65F6\u4F7F\u7528\u7684\u76EE\u6807 CPA\uFF08\u5143\uFF09",
|
|
107779
|
+
(v) => parseFloat(v)
|
|
107780
|
+
).option("--apply", "\u5BF9\u547D\u4E2D\u5BF9\u8C61\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("--json-out <path>", "\u843D\u76D8\u76EE\u5F55\u6216 *.json", void 0).option("--refresh-dp", "\u5F3A\u5236\u91CD\u62C9 Datapermission", false).option("--verbose", "\u8BE6\u7EC6\u9519\u8BEF", false).action(
|
|
107781
|
+
async (opts) => {
|
|
107782
|
+
await runZeroConv({
|
|
107783
|
+
media: opts.media,
|
|
107784
|
+
accounts: opts.accounts,
|
|
107785
|
+
date: opts.date,
|
|
107786
|
+
cpaMultiple: opts.cpaMultiple,
|
|
107787
|
+
level: opts.level,
|
|
107788
|
+
fallbackTargetCpa: opts.fallbackTargetCpa,
|
|
107789
|
+
apply: opts.apply,
|
|
107790
|
+
pageSize: opts.pageSize,
|
|
107791
|
+
maxPages: opts.maxPages,
|
|
107792
|
+
token: opts.token,
|
|
107793
|
+
jsonOut: opts.jsonOut,
|
|
107794
|
+
refreshDp: opts.refreshDp,
|
|
107795
|
+
verbose: opts.verbose
|
|
107796
|
+
});
|
|
107797
|
+
}
|
|
107798
|
+
);
|
|
107799
|
+
}
|
|
107800
|
+
|
|
107058
107801
|
// src/commands/stats.ts
|
|
107059
107802
|
init_auth();
|
|
107060
107803
|
init_cli_json_snapshot();
|
|
@@ -107307,7 +108050,7 @@ ${opts.media} \u6295\u653E\u6570\u636E\uFF08${rangeLabel}\uFF09\uFF08\u7B2C 1 \u
|
|
|
107307
108050
|
printCliTable(rows, cols);
|
|
107308
108051
|
console.log();
|
|
107309
108052
|
}
|
|
107310
|
-
function
|
|
108053
|
+
function register12(program2) {
|
|
107311
108054
|
program2.command("stats").description(
|
|
107312
108055
|
"\u67E5\u8BE2\u5E7F\u544A\u6D88\u8017\u3001\u70B9\u51FB\u3001\u8F6C\u5316\u7B49\u6295\u653E\u6570\u636E\uFF08\u9ED8\u8BA4\u8FD1 7 \u5929\u533A\u95F4\u5408\u8BA1\uFF0C\u4E0D\u542B\u4ECA\u5929\uFF0C\u65E5\u5386\u6309 UTC+8\uFF1B\u8981\u6309\u65E5\u660E\u7EC6\u52A0 --by-day\uFF09"
|
|
107313
108056
|
).requiredOption(
|
|
@@ -107426,7 +108169,7 @@ async function runAccountHistory(opts) {
|
|
|
107426
108169
|
}
|
|
107427
108170
|
console.log();
|
|
107428
108171
|
}
|
|
107429
|
-
function
|
|
108172
|
+
function register13(program2) {
|
|
107430
108173
|
program2.command("account-history").description("\u67E5\u8BE2\u5F00\u6237\u7533\u8BF7\u5386\u53F2\u8BB0\u5F55").option("-m, --media <type>", "\u5A92\u4F53\u7C7B\u578B\uFF1AGoogle | TikTok | MetaAd | BingV2 | Kwai").option("-s, --status <status>", "\u7533\u8BF7\u72B6\u6001\u5173\u952E\u5B57").option("-k, --keyword <text>", "\u641C\u7D22\u5173\u952E\u5B57\uFF08\u8D26\u6237\u540D/ID\uFF09").option("--start <date>", "\u5F00\u59CB\u65E5\u671F YYYY-MM-DD").option("--end <date>", "\u7ED3\u675F\u65E5\u671F YYYY-MM-DD").option("-p, --page <n>", "\u9875\u7801", parseInt).option("--page-size <n>", "\u6BCF\u9875\u6570\u91CF\uFF08\u9ED8\u8BA4 20\uFF09", parseInt).option("-t, --token <token>", "Auth Token").option(
|
|
107431
108174
|
"--json-out <path>",
|
|
107432
108175
|
"\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",
|
|
@@ -108268,7 +109011,7 @@ async function runReportPushReceiveEmails(opts) {
|
|
|
108268
109011
|
var splitCsv = (v) => v.split(",").map((s) => s.trim()).filter(Boolean);
|
|
108269
109012
|
|
|
108270
109013
|
// src/commands/report/register.ts
|
|
108271
|
-
function
|
|
109014
|
+
function register14(program2) {
|
|
108272
109015
|
const reportCmd = program2.command("report").description("\u4F18\u5316\u62A5\u544A\u4E0E\u63A8\u9001\u7BA1\u7406");
|
|
108273
109016
|
reportCmd.command("list").description("\u67E5\u8BE2\u4F18\u5316\u62A5\u544A\u5217\u8868").requiredOption("-m, --media <type>", "\u5A92\u4F53\u7C7B\u578B\uFF1AGoogle | TikTok").option("-t, --type <type>", "\u62A5\u544A\u7C7B\u578B\uFF08\u5982 Daily / Hourly\uFF09").option("-s, --status <ready>", "\u751F\u6210\u72B6\u6001\uFF08true/false\uFF09").option("-k, --keyword <text>", "\u62A5\u544A\u540D\u79F0\u5173\u952E\u5B57").option("--start <date>", "\u5F00\u59CB\u65E5\u671F").option("--end <date>", "\u7ED3\u675F\u65E5\u671F").option("-p, --page <n>", "\u9875\u7801", parseInt).option("--page-size <n>", "\u6BCF\u9875\u6570\u91CF", parseInt).option("--token <token>", "Auth Token").option(
|
|
108274
109017
|
"--json-out <path>",
|
|
@@ -108596,7 +109339,7 @@ async function runTransferList(opts) {
|
|
|
108596
109339
|
printCliTable(rows, cols);
|
|
108597
109340
|
console.log();
|
|
108598
109341
|
}
|
|
108599
|
-
function
|
|
109342
|
+
function register15(program2) {
|
|
108600
109343
|
const transferCmd = program2.command("transfer").description("\u5A92\u4F53\u8D26\u6237\u8F6C\u8D26\uFF1A\u63D0\u4EA4\u8F6C\u8D26\u7533\u8BF7\u6216\u67E5\u8BE2\u8F6C\u8D26\u8BB0\u5F55");
|
|
108601
109344
|
transferCmd.command("create").description("\u63D0\u4EA4\u8D26\u6237\u95F4\u8F6C\u8D26\u7533\u8BF7\uFF08Google / TikTok / MetaAd / BingV2\uFF09").requiredOption("-m, --media <type>", "\u5A92\u4F53\u7C7B\u578B\uFF1AGoogle | TikTok | MetaAd | BingV2").requiredOption("--out <id>", "\u8F6C\u51FA\u8D26\u6237 mediaCustomerId\uFF08\u6765\u81EA list-accounts\uFF09").requiredOption("--in <id>", "\u8F6C\u5165\u8D26\u6237 mediaCustomerId\uFF08\u6765\u81EA list-accounts\uFF09").requiredOption("--amount <amount>", "\u8F6C\u8D26\u91D1\u989D\uFF08\u4E0E\u8D26\u6237\u8D27\u5E01\u5355\u4F4D\u4E00\u81F4\uFF09").option("--customer-name <name>", "\u5BA2\u6237\u540D\u79F0\uFF08\u5907\u6CE8\uFF0C\u53EF\u9009\uFF09").option("-t, --token <token>", "Auth Token").option(
|
|
108602
109345
|
"--json-out <path>",
|
|
@@ -109041,7 +109784,7 @@ async function runInvoiceApply(opts) {
|
|
|
109041
109784
|
}
|
|
109042
109785
|
|
|
109043
109786
|
// src/commands/invoice/_register.ts
|
|
109044
|
-
function
|
|
109787
|
+
function register16(program2) {
|
|
109045
109788
|
const invoiceCmd = program2.command("invoice").description("\u5F00\u7968\u8BB0\u5F55\u4E0E\u5F00\u7968\u7533\u8BF7\uFF0C\u4E0D\u652F\u6301\u8D26\u6237\u6FC0\u6D3B\u5145\u503C\u8BA2\u5355\u7684\u5F00\u7968\uFF0C\u5982\u9700\u5F00\u7968\u8BF7\u8054\u7CFB\u7EBF\u4E0B\u8FD0\u8425");
|
|
109046
109789
|
invoiceCmd.command("list").description("\u67E5\u8BE2\u5F00\u7968\u7533\u8BF7\u8BB0\u5F55").option("-k, --keyword <text>", "\u53D1\u7968\u53F7\u6216\u5173\u952E\u5B57").option("--start <date>", "\u5F00\u59CB\u65E5\u671F YYYY-MM-DD").option("--end <date>", "\u7ED3\u675F\u65E5\u671F YYYY-MM-DD").option("-p, --page <n>", "\u9875\u7801", parseInt).option("--page-size <n>", "\u6BCF\u9875\u6570\u91CF", parseInt).option("-t, --token <token>", "Auth Token").option(
|
|
109047
109790
|
"--json-out <path>",
|
|
@@ -109312,7 +110055,7 @@ async function runInvoiceInfoDelete(opts) {
|
|
|
109312
110055
|
\u2705 \u53D1\u7968\u62AC\u5934\u5DF2\u5220\u9664\uFF08ID: ${opts.id}\uFF09
|
|
109313
110056
|
`);
|
|
109314
110057
|
}
|
|
109315
|
-
function
|
|
110058
|
+
function register17(program2) {
|
|
109316
110059
|
const invoiceInfoCmd = program2.command("invoice-info").description("\u53D1\u7968\u62AC\u5934\u7BA1\u7406\uFF08\u5BF9\u5E94\u300C\u8BBE\u7F6E \u2192 \u53D1\u7968\u62AC\u5934\u300D\u9875\u9762\uFF09");
|
|
109317
110060
|
invoiceInfoCmd.command("list").description("\u67E5\u8BE2\u5DF2\u4FDD\u5B58\u7684\u53D1\u7968\u62AC\u5934\u5217\u8868").option("-k, --keyword <text>", "\u6309\u516C\u53F8\u540D\u79F0\u641C\u7D22").option("--invoice-type <type>", "\u6309\u53D1\u7968\u7C7B\u578B\u7B5B\u9009\uFF1API | VATI | VATSI").option("--page <n>", "\u9875\u7801", (v) => parseInt(v), 1).option("--page-size <n>", "\u6BCF\u9875\u6761\u6570", (v) => parseInt(v), 20).option("-t, --token <token>", "Auth Token").option(
|
|
109318
110061
|
"--json-out <path>",
|
|
@@ -109660,7 +110403,7 @@ async function runOptimizeChildren(opts) {
|
|
|
109660
110403
|
}
|
|
109661
110404
|
|
|
109662
110405
|
// src/commands/optimize/_register.ts
|
|
109663
|
-
function
|
|
110406
|
+
function register18(program2) {
|
|
109664
110407
|
const optimizeCmd = program2.command("optimize").description("AI \u5E7F\u544A\u4F18\u5316\uFF08Google\uFF09");
|
|
109665
110408
|
optimizeCmd.command("list").description("\u67E5\u8BE2\u8D26\u6237\u7EA7\u4F18\u5316\u4E3B\u5217\u8868\uFF08\u5BF9\u5E94\u9875\u9762 /advertising/intelligentOptimization\uFF09").option("-a, --account <id>", "\u8D26\u6237 mediaCustomerId\uFF08\u7559\u7A7A\u67E5\u5168\u90E8\uFF09").option(
|
|
109666
110409
|
"--match-media-customer-id <id>",
|
|
@@ -110106,7 +110849,7 @@ async function runForewarningCreate(opts) {
|
|
|
110106
110849
|
}
|
|
110107
110850
|
|
|
110108
110851
|
// src/commands/forewarning/_register.ts
|
|
110109
|
-
function
|
|
110852
|
+
function register19(program2) {
|
|
110110
110853
|
const forewarningCmd = program2.command("forewarning").description("\u667A\u80FD\u9884\u8B66\u89C4\u5219\u4E0E\u9884\u8B66\u8BB0\u5F55\u7BA1\u7406");
|
|
110111
110854
|
forewarningCmd.command("list").description("\u67E5\u8BE2\u9884\u8B66\u89C4\u5219\u5217\u8868").requiredOption("-m, --media <type>", "\u5A92\u4F53\u7C7B\u578B\uFF1AGoogle | TikTok").option("-a, --account <id>", "\u8D26\u6237 ID \u7B5B\u9009").option("-k, --keyword <text>", "\u89C4\u5219\u540D\u79F0\u5173\u952E\u5B57").option("--start <date>", "\u5F00\u59CB\u65E5\u671F YYYY-MM-DD").option("--end <date>", "\u7ED3\u675F\u65E5\u671F YYYY-MM-DD").option("-p, --page <n>", "\u9875\u7801", parseInt).option("--page-size <n>", "\u6BCF\u9875\u6570\u91CF", parseInt).option("-t, --token <token>", "Auth Token").option(
|
|
110112
110855
|
"--json-out <path>",
|
|
@@ -110398,7 +111141,7 @@ Meta \u7EBF\u7D22\u8868\u5355\uFF08\u8D26\u6237 ${opts.account}\uFF0C\u7B2C 1 \u
|
|
|
110398
111141
|
console.log();
|
|
110399
111142
|
}
|
|
110400
111143
|
}
|
|
110401
|
-
function
|
|
111144
|
+
function register20(program2) {
|
|
110402
111145
|
program2.command("clue").description("\u67E5\u8BE2\u5E7F\u544A\u7EBF\u7D22\u8868\u5355\u6570\u636E\uFF08TikTok / Meta\uFF09").option("-m, --media <type>", "\u5A92\u4F53\u7C7B\u578B\uFF1ATikTok | Meta", "TikTok").requiredOption("-a, --account <id>", "\u5E7F\u544A\u8D26\u6237 mediaCustomerId").option("--region <region>", "TikTok \u533A\u57DF\uFF1Aeu | us | other | ALL\uFF08\u9ED8\u8BA4 ALL\uFF09").option("--start <date>", "Meta \u5F00\u59CB\u65E5\u671F\uFF08YYYY-MM-DD\uFF09").option("--end <date>", "Meta \u7ED3\u675F\u65E5\u671F\uFF08YYYY-MM-DD\uFF09").option(
|
|
110403
111146
|
"--json-out <path>",
|
|
110404
111147
|
"\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",
|
|
@@ -110417,80 +111160,6 @@ function register19(program2) {
|
|
|
110417
111160
|
});
|
|
110418
111161
|
}
|
|
110419
111162
|
|
|
110420
|
-
// src/commands/ad/_shared.ts
|
|
110421
|
-
init_auth();
|
|
110422
|
-
async function findItemInList(listUrl, config, id, verbose) {
|
|
110423
|
-
const res = await apiFetch2(listUrl, config, {}, verbose);
|
|
110424
|
-
const item = (res.data ?? []).find((x) => x["id"] === id);
|
|
110425
|
-
if (!item) {
|
|
110426
|
-
throw new Error(`\u672A\u5728\u5217\u8868\u4E2D\u627E\u5230 id="${id}" \u7684\u6761\u76EE`);
|
|
110427
|
-
}
|
|
110428
|
-
return item;
|
|
110429
|
-
}
|
|
110430
|
-
function toGoogleDate(dateStr, offsetDays) {
|
|
110431
|
-
if (dateStr) return dateStr.replace(/-/g, "/");
|
|
110432
|
-
const d = /* @__PURE__ */ new Date();
|
|
110433
|
-
d.setDate(d.getDate() + offsetDays);
|
|
110434
|
-
return d.toISOString().slice(0, 10).replace(/-/g, "/");
|
|
110435
|
-
}
|
|
110436
|
-
function toDisplayMoney(raw) {
|
|
110437
|
-
if (raw == null) return null;
|
|
110438
|
-
const n = Number(raw);
|
|
110439
|
-
if (!Number.isFinite(n)) return null;
|
|
110440
|
-
return n / 100;
|
|
110441
|
-
}
|
|
110442
|
-
function toCentAmount(displayValue) {
|
|
110443
|
-
if (!Number.isFinite(displayValue)) {
|
|
110444
|
-
throw new Error(`toCentAmount: \u8F93\u5165\u5FC5\u987B\u662F\u6709\u9650\u6570\u5B57\uFF0C\u6536\u5230 ${displayValue}`);
|
|
110445
|
-
}
|
|
110446
|
-
return Math.round(displayValue * 100);
|
|
110447
|
-
}
|
|
110448
|
-
var CAMPAIGN_BATCH_MONEY_KEYS = /* @__PURE__ */ new Set([
|
|
110449
|
-
"Budget",
|
|
110450
|
-
"MaxCPCAmount",
|
|
110451
|
-
"MaxCpmAmount",
|
|
110452
|
-
"MaxCPVAmount",
|
|
110453
|
-
"TargetCpa_BidingAmount",
|
|
110454
|
-
"TargetSpend_BidCeilingAmount",
|
|
110455
|
-
"TargetCpaAmount",
|
|
110456
|
-
"MaxCPC"
|
|
110457
|
-
]);
|
|
110458
|
-
function convertBatchMoneyTreeFromYuan(value) {
|
|
110459
|
-
return convertBatchMoneyTreeFromYuanInner(value);
|
|
110460
|
-
}
|
|
110461
|
-
function convertBatchMoneyTreeFromYuanInner(value) {
|
|
110462
|
-
if (Array.isArray(value)) {
|
|
110463
|
-
return value.map(convertBatchMoneyTreeFromYuanInner);
|
|
110464
|
-
}
|
|
110465
|
-
if (value && typeof value === "object") {
|
|
110466
|
-
const out = {};
|
|
110467
|
-
for (const [key, val] of Object.entries(value)) {
|
|
110468
|
-
if (typeof val === "number" && CAMPAIGN_BATCH_MONEY_KEYS.has(key)) {
|
|
110469
|
-
out[key] = toCentAmount(val);
|
|
110470
|
-
} else {
|
|
110471
|
-
out[key] = convertBatchMoneyTreeFromYuanInner(val);
|
|
110472
|
-
}
|
|
110473
|
-
}
|
|
110474
|
-
return out;
|
|
110475
|
-
}
|
|
110476
|
-
return value;
|
|
110477
|
-
}
|
|
110478
|
-
function adgroupListUrl(googleApiUrl, account, startDate, endDate) {
|
|
110479
|
-
const params = new URLSearchParams();
|
|
110480
|
-
params.set("startDate", toGoogleDate(startDate, -30));
|
|
110481
|
-
params.set("endDate", toGoogleDate(endDate, 0));
|
|
110482
|
-
return `${googleApiUrl}/adgroupnmanagement/v2/list/${account}?${params}`;
|
|
110483
|
-
}
|
|
110484
|
-
function requireGoogleApi(config) {
|
|
110485
|
-
if (!config.googleApiUrl) {
|
|
110486
|
-
console.error(
|
|
110487
|
-
"\n\u274C \u672A\u627E\u5230 Google API \u5730\u5740\u3002\n Google API \u5730\u5740\u4ECE TSO API \u5730\u5740\u81EA\u52A8\u63A8\u5BFC\uFF0C\u4E5F\u53EF\u901A\u8FC7\u73AF\u5883\u53D8\u91CF SILUZAN_GOOGLE_API \u8986\u76D6\u3002\n"
|
|
110488
|
-
);
|
|
110489
|
-
process.exit(1);
|
|
110490
|
-
}
|
|
110491
|
-
return config.googleApiUrl;
|
|
110492
|
-
}
|
|
110493
|
-
|
|
110494
111163
|
// src/commands/ad/campaign.ts
|
|
110495
111164
|
init_auth();
|
|
110496
111165
|
init_cli_json_snapshot();
|
|
@@ -119638,7 +120307,7 @@ function parseOptionalMatchType(raw) {
|
|
|
119638
120307
|
`);
|
|
119639
120308
|
process.exit(1);
|
|
119640
120309
|
}
|
|
119641
|
-
function
|
|
120310
|
+
function register21(program2) {
|
|
119642
120311
|
const adCmd = program2.command("ad").description(
|
|
119643
120312
|
"Google \u5E7F\u544A\u7BA1\u7406\uFF08Google API \u5730\u5740\u4ECE TSO API \u81EA\u52A8\u63A8\u5BFC\uFF0C\u53EF\u901A\u8FC7 SILUZAN_GOOGLE_API \u73AF\u5883\u53D8\u91CF\u8986\u76D6\uFF09\n \u542B\uFF1A\u7CFB\u5217/\u7EC4/\u521B\u610F/\u5173\u952E\u8BCD\u7B49\u5E38\u89C4\u5E7F\u544A\u6295\u653E\u4E0E\u7BA1\u7406\u80FD\u529B"
|
|
119644
120313
|
);
|
|
@@ -121311,7 +121980,7 @@ function attachBaseCliOptions(cmd) {
|
|
|
121311
121980
|
function splitKeywordSeeds(raw) {
|
|
121312
121981
|
return raw.split(",").map((s) => s.trim()).filter(Boolean);
|
|
121313
121982
|
}
|
|
121314
|
-
function
|
|
121983
|
+
function register22(program2) {
|
|
121315
121984
|
const keywordCmd = program2.command("keyword").description(
|
|
121316
121985
|
"Google \u5173\u952E\u5B57\u63A8\u8350\uFF08\u53EF\u9009 -a \u8D26\u6237 ID \u6309\u8D26\u6237\u5E01\u79CD\u51FA\u4EF7\uFF1B\u5426\u5219\u5171\u4EAB MCC/USD\uFF1B\u53EF --geo \u9650\u5B9A\u5E02\u573A\uFF09"
|
|
121317
121986
|
);
|
|
@@ -121780,7 +122449,7 @@ async function runRagList(options) {
|
|
|
121780
122449
|
];
|
|
121781
122450
|
console.log(lines.join("\n"));
|
|
121782
122451
|
}
|
|
121783
|
-
function
|
|
122452
|
+
function register23(program2) {
|
|
121784
122453
|
const ragCmd = program2.command("rag").description(
|
|
121785
122454
|
"RAG \u77E5\u8BC6\u5E93\u68C0\u7D22\uFF08\u534F\u52A9\u5E7F\u544A\u6295\u653E\u3001\u62D3\u8BCD\u3001\u8D26\u6237\u8BCA\u65AD\u4E0E\u62A5\u544A\uFF1B\u9ED8\u8BA4 belongToId=account/me \u7684 companyId\uFF09"
|
|
121786
122455
|
);
|
|
@@ -123231,7 +123900,7 @@ async function runAccountBmBind(opts) {
|
|
|
123231
123900
|
}
|
|
123232
123901
|
|
|
123233
123902
|
// src/commands/account-manage-register.ts
|
|
123234
|
-
function
|
|
123903
|
+
function register24(program2) {
|
|
123235
123904
|
const accountCmd = program2.command("account").description("\u5E7F\u544A\u8D26\u6237\u7BA1\u7406\uFF1AOAuth \u6DFB\u52A0\u6388\u6743\u3001\u89E3\u9664\u5173\u8054\uFF08\u89E3\u7ED1\uFF09\u3001Google MCC \u7ED1\u5B9A/\u89E3\u7ED1\u3001\u8D26\u53F7\u5206\u4EAB");
|
|
123236
123905
|
accountCmd.command("list", { hidden: true }).description("\uFF08\u517C\u5BB9\u522B\u540D\uFF09\u540C list-accounts").option(
|
|
123237
123906
|
"-m, --media <type>",
|
|
@@ -125061,7 +125730,7 @@ async function runOpenAccountMeta(opts) {
|
|
|
125061
125730
|
}
|
|
125062
125731
|
|
|
125063
125732
|
// src/commands/open-account-register.ts
|
|
125064
|
-
function
|
|
125733
|
+
function register25(program2) {
|
|
125065
125734
|
const openAccountCmd = program2.command("open-account").description("\u5F00\u6237\u7533\u8BF7\uFF08Google / TikTok / Yandex / Bing / Kwai / Meta \u5F00\u6237\u94FE\u63A5\uFF09");
|
|
125066
125735
|
openAccountCmd.command("meta").description(
|
|
125067
125736
|
"\u83B7\u53D6 Meta/Facebook \u5B98\u65B9\u5F00\u6237\u94FE\u63A5\uFF08GET MetaAccount/Management/GetOpenAccountLink\uFF0C\u4E0E\u7F51\u9875\u300C\u7533\u8BF7\u5F00\u6237\u300D\u4E00\u81F4\uFF09"
|
|
@@ -125285,7 +125954,7 @@ function register24(program2) {
|
|
|
125285
125954
|
}
|
|
125286
125955
|
|
|
125287
125956
|
// src/commands/diagnostic.ts
|
|
125288
|
-
function
|
|
125957
|
+
function register26(program2) {
|
|
125289
125958
|
program2.command("diagnostic").description("\u8BCA\u65AD\u5165\u53E3\u8BF4\u660E\uFF1AGoogle \u5E7F\u544A\u8D26\u6237\u8BCA\u65AD\u9875 vs \u7F51\u7AD9\u8BCA\u65AD CLI").action(() => {
|
|
125290
125959
|
console.log("\n\u8BCA\u65AD\u80FD\u529B\u8BF4\u660E\n");
|
|
125291
125960
|
console.log(" \u3010Google \u5E7F\u544A\u8D26\u6237\u8BCA\u65AD\u3011");
|
|
@@ -129055,12 +129724,12 @@ async function mergeFacebookSnapshotIntoReport(payload, snapshotDir) {
|
|
|
129055
129724
|
const accountId = manifest.accountId ?? (typeof overview?.accountId === "string" ? overview.accountId : void 0);
|
|
129056
129725
|
const kpis = { ...payload.kpis ?? {} };
|
|
129057
129726
|
if (current) {
|
|
129058
|
-
if (
|
|
129059
|
-
if (
|
|
129060
|
-
if (
|
|
129061
|
-
if (
|
|
129062
|
-
if (
|
|
129063
|
-
if (
|
|
129727
|
+
if (current.spend != null) kpis.spend = current.spend;
|
|
129728
|
+
if (current.results != null) kpis.results = current.results;
|
|
129729
|
+
if (current.costPerResult != null) kpis.costPerResult = current.costPerResult;
|
|
129730
|
+
if (current.reach != null) kpis.reach = current.reach;
|
|
129731
|
+
if (current.impressions != null) kpis.impressions = current.impressions;
|
|
129732
|
+
if (current.frequency != null) kpis.frequency = current.frequency;
|
|
129064
129733
|
}
|
|
129065
129734
|
const meta = { ...payload.meta ?? {} };
|
|
129066
129735
|
if (!meta.accountName && accountName) meta.accountName = accountName;
|
|
@@ -129073,12 +129742,12 @@ async function mergeFacebookSnapshotIntoReport(payload, snapshotDir) {
|
|
|
129073
129742
|
if (!meta.resultType && current?.resultType) meta.resultType = String(current.resultType);
|
|
129074
129743
|
const charts = { ...payload.charts ?? {} };
|
|
129075
129744
|
const avgCpl = num5(kpis.costPerResult) ?? 0;
|
|
129076
|
-
|
|
129745
|
+
{
|
|
129077
129746
|
const platform = sectionMap.get("platform");
|
|
129078
129747
|
const networks = Array.isArray(platform?.networks) ? platform.networks : [];
|
|
129079
129748
|
if (networks.length) charts.platform = aggregatePlatform(networks);
|
|
129080
129749
|
}
|
|
129081
|
-
|
|
129750
|
+
{
|
|
129082
129751
|
const country = sectionMap.get("country");
|
|
129083
129752
|
const countries = Array.isArray(country?.countries) ? country.countries : [];
|
|
129084
129753
|
if (countries.length) {
|
|
@@ -129089,7 +129758,7 @@ async function mergeFacebookSnapshotIntoReport(payload, snapshotDir) {
|
|
|
129089
129758
|
};
|
|
129090
129759
|
}
|
|
129091
129760
|
}
|
|
129092
|
-
|
|
129761
|
+
{
|
|
129093
129762
|
const audience = sectionMap.get("audience");
|
|
129094
129763
|
const audiences = Array.isArray(audience?.audiences) ? audience.audiences : [];
|
|
129095
129764
|
if (audiences.length) {
|
|
@@ -129101,11 +129770,11 @@ async function mergeFacebookSnapshotIntoReport(payload, snapshotDir) {
|
|
|
129101
129770
|
};
|
|
129102
129771
|
}
|
|
129103
129772
|
}
|
|
129104
|
-
if (
|
|
129773
|
+
if (num5(kpis.reach) != null) {
|
|
129105
129774
|
charts.funnel = { reach: kpis.reach, results: kpis.results ?? 0 };
|
|
129106
129775
|
}
|
|
129107
129776
|
const tables = { ...payload.tables ?? {} };
|
|
129108
|
-
|
|
129777
|
+
{
|
|
129109
129778
|
const adSets = sectionMap.get("ad-sets");
|
|
129110
129779
|
const groups = Array.isArray(adSets?.adGroups) ? adSets.adGroups : [];
|
|
129111
129780
|
if (groups.length) {
|
|
@@ -129128,15 +129797,13 @@ async function mergeFacebookSnapshotIntoReport(payload, snapshotDir) {
|
|
|
129128
129797
|
}).sort((a, b) => b.spend - a.spend);
|
|
129129
129798
|
}
|
|
129130
129799
|
}
|
|
129131
|
-
|
|
129800
|
+
{
|
|
129132
129801
|
const audience = sectionMap.get("audience");
|
|
129133
129802
|
const audiences = Array.isArray(audience?.audiences) ? audience.audiences : [];
|
|
129134
129803
|
if (audiences.length) {
|
|
129135
129804
|
const rows = buildAudienceRows(audiences).sort((a, b) => a.costPerResult - b.costPerResult);
|
|
129136
|
-
|
|
129137
|
-
|
|
129138
|
-
if (!tables.audienceTop) tables.audienceTop = topN;
|
|
129139
|
-
if (!tables.audienceBottom) tables.audienceBottom = bottomN;
|
|
129805
|
+
tables.audienceTop = rows.slice(0, 10);
|
|
129806
|
+
tables.audienceBottom = [...rows].reverse().slice(0, 10);
|
|
129140
129807
|
}
|
|
129141
129808
|
}
|
|
129142
129809
|
const currency = typeof meta.currency === "string" ? meta.currency : "USD";
|
|
@@ -129851,17 +130518,26 @@ async function mergeMetaDiagnosisSnapshotIntoReport(payload, snapshotDir) {
|
|
|
129851
130518
|
if (!meta.generatedAt) meta.generatedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
129852
130519
|
const kpis = { ...payload.kpis ?? {} };
|
|
129853
130520
|
if (currentPeriod) {
|
|
129854
|
-
|
|
129855
|
-
|
|
129856
|
-
|
|
129857
|
-
|
|
129858
|
-
|
|
129859
|
-
|
|
129860
|
-
|
|
129861
|
-
|
|
129862
|
-
|
|
129863
|
-
|
|
129864
|
-
|
|
130521
|
+
const spend = num6(currentPeriod.spend);
|
|
130522
|
+
const impressions = num6(currentPeriod.impressions);
|
|
130523
|
+
const clicks = num6(currentPeriod.clicks);
|
|
130524
|
+
const results = num6(currentPeriod.results);
|
|
130525
|
+
const costPerResult = num6(currentPeriod.costPerResult);
|
|
130526
|
+
const ctr = num6(currentPeriod.ctr);
|
|
130527
|
+
const cpc = num6(currentPeriod.cpc ?? currentPeriod.averageCpc);
|
|
130528
|
+
const reach = num6(currentPeriod.reach);
|
|
130529
|
+
const frequency = num6(currentPeriod.frequency);
|
|
130530
|
+
if (spend != null) kpis.spend = spend;
|
|
130531
|
+
if (impressions != null) kpis.impressions = impressions;
|
|
130532
|
+
if (clicks != null) kpis.clicks = clicks;
|
|
130533
|
+
if (results != null) kpis.results = results;
|
|
130534
|
+
if (costPerResult != null) kpis.costPerResult = costPerResult;
|
|
130535
|
+
if (ctr != null) kpis.ctr = ctr;
|
|
130536
|
+
if (cpc != null) kpis.cpc = cpc;
|
|
130537
|
+
if (reach != null) kpis.reach = reach;
|
|
130538
|
+
if (frequency != null) kpis.frequency = frequency;
|
|
130539
|
+
}
|
|
130540
|
+
if (previousPeriod) {
|
|
129865
130541
|
kpis.previousPeriod = {
|
|
129866
130542
|
spend: num6(previousPeriod.spend),
|
|
129867
130543
|
impressions: num6(previousPeriod.impressions),
|
|
@@ -129875,7 +130551,7 @@ async function mergeMetaDiagnosisSnapshotIntoReport(payload, snapshotDir) {
|
|
|
129875
130551
|
};
|
|
129876
130552
|
}
|
|
129877
130553
|
const tables = { ...payload.tables ?? {} };
|
|
129878
|
-
|
|
130554
|
+
{
|
|
129879
130555
|
const rows = asRows(sectionMap.get("ad-sets")?.adGroups).map(
|
|
129880
130556
|
(r) => pickMetricExtras(r, [
|
|
129881
130557
|
"adGroupId",
|
|
@@ -129886,38 +130562,38 @@ async function mergeMetaDiagnosisSnapshotIntoReport(payload, snapshotDir) {
|
|
|
129886
130562
|
);
|
|
129887
130563
|
if (rows.length > 0) tables.adSets = rows;
|
|
129888
130564
|
}
|
|
129889
|
-
|
|
130565
|
+
{
|
|
129890
130566
|
const rows = asRows(sectionMap.get("platform")?.networks).map(
|
|
129891
130567
|
(r) => pickMetricExtras(r, ["publisherPlatform", "platformPosition", "network"])
|
|
129892
130568
|
);
|
|
129893
130569
|
if (rows.length > 0) tables.platform = rows;
|
|
129894
130570
|
}
|
|
129895
|
-
|
|
130571
|
+
{
|
|
129896
130572
|
const rows = asRows(sectionMap.get("country")?.countries).map(
|
|
129897
130573
|
(r) => pickMetricExtras(r, ["countryOrRegion"])
|
|
129898
130574
|
);
|
|
129899
130575
|
if (rows.length > 0) tables.country = rows;
|
|
129900
130576
|
}
|
|
129901
|
-
|
|
130577
|
+
{
|
|
129902
130578
|
const rows = asRows(sectionMap.get("audience")?.audiences).map(
|
|
129903
130579
|
(r) => pickMetricExtras(r, ["age", "gender"])
|
|
129904
130580
|
);
|
|
129905
130581
|
if (rows.length > 0) tables.audience = rows;
|
|
129906
130582
|
}
|
|
129907
|
-
|
|
130583
|
+
{
|
|
129908
130584
|
const rows = asRows(sectionMap.get("creative")?.creatives).map(
|
|
129909
130585
|
(r) => pickMetricExtras(r, ["adId", "adName", "creativeType", "title"])
|
|
129910
130586
|
);
|
|
129911
130587
|
if (rows.length > 0) tables.creative = rows;
|
|
129912
130588
|
}
|
|
129913
|
-
|
|
130589
|
+
{
|
|
129914
130590
|
const mat = sectionMap.get("material");
|
|
129915
130591
|
const rows = asRows(mat?.materials).map(
|
|
129916
130592
|
(r) => pickMetricExtras(r, ["materialId", "materialName", "materialType"])
|
|
129917
130593
|
);
|
|
129918
130594
|
if (rows.length > 0) {
|
|
129919
130595
|
tables.material = rows;
|
|
129920
|
-
if (
|
|
130596
|
+
if (mat) {
|
|
129921
130597
|
tables.materialMeta = {
|
|
129922
130598
|
dataSource: mat.dataSource,
|
|
129923
130599
|
message: mat.message
|
|
@@ -130220,7 +130896,7 @@ function resolveSectionList2(sections, exclude) {
|
|
|
130220
130896
|
}
|
|
130221
130897
|
|
|
130222
130898
|
// src/commands/facebook-analysis/run-batch.ts
|
|
130223
|
-
async function
|
|
130899
|
+
async function runWithConcurrency3(tasks, concurrency) {
|
|
130224
130900
|
const results = new Array(tasks.length);
|
|
130225
130901
|
let cursor = 0;
|
|
130226
130902
|
const workers = new Array(Math.max(1, Math.min(concurrency, tasks.length))).fill(0).map(async () => {
|
|
@@ -130284,7 +130960,7 @@ async function runAllFacebookSections(opts) {
|
|
|
130284
130960
|
}
|
|
130285
130961
|
}
|
|
130286
130962
|
);
|
|
130287
|
-
const results = await
|
|
130963
|
+
const results = await runWithConcurrency3(tasks, concurrency);
|
|
130288
130964
|
const succeeded = results.filter((r) => r.ok).length;
|
|
130289
130965
|
const failed = results.length - succeeded;
|
|
130290
130966
|
const absoluteSnapshotDir = path35.resolve(opts.jsonOut);
|
|
@@ -130796,7 +131472,7 @@ init_auth();
|
|
|
130796
131472
|
import * as path36 from "path";
|
|
130797
131473
|
import { performance as performance6 } from "perf_hooks";
|
|
130798
131474
|
init_version();
|
|
130799
|
-
async function
|
|
131475
|
+
async function runWithConcurrency4(tasks, concurrency) {
|
|
130800
131476
|
const results = new Array(tasks.length);
|
|
130801
131477
|
let cursor = 0;
|
|
130802
131478
|
const workers = new Array(Math.max(1, Math.min(concurrency, tasks.length))).fill(0).map(async () => {
|
|
@@ -130864,7 +131540,7 @@ async function runAllBingSections(opts) {
|
|
|
130864
131540
|
};
|
|
130865
131541
|
}
|
|
130866
131542
|
});
|
|
130867
|
-
const results = await
|
|
131543
|
+
const results = await runWithConcurrency4(tasks, concurrency);
|
|
130868
131544
|
const succeeded = results.filter((r) => r.ok).length;
|
|
130869
131545
|
const failed = results.length - succeeded;
|
|
130870
131546
|
const absoluteSnapshotDir = path36.resolve(opts.jsonOut);
|
|
@@ -131128,25 +131804,27 @@ async function mergeBingAnalysisSnapshotIntoReport(payload, snapshotDir) {
|
|
|
131128
131804
|
costPerConversion: num8(currentPeriod.costPerConversion)
|
|
131129
131805
|
} : null);
|
|
131130
131806
|
if (currentKpiSource) {
|
|
131131
|
-
if (
|
|
131132
|
-
if (
|
|
131133
|
-
if (
|
|
131134
|
-
if (
|
|
131135
|
-
if (
|
|
131136
|
-
if (
|
|
131137
|
-
if (
|
|
131138
|
-
|
|
131139
|
-
|
|
131140
|
-
|
|
131141
|
-
|
|
131142
|
-
|
|
131143
|
-
|
|
131144
|
-
|
|
131145
|
-
|
|
131146
|
-
|
|
131147
|
-
kpis.
|
|
131148
|
-
|
|
131149
|
-
|
|
131807
|
+
if (currentKpiSource.spend != null) kpis.spend = currentKpiSource.spend;
|
|
131808
|
+
if (currentKpiSource.impressions != null) kpis.impressions = currentKpiSource.impressions;
|
|
131809
|
+
if (currentKpiSource.clicks != null) kpis.clicks = currentKpiSource.clicks;
|
|
131810
|
+
if (currentKpiSource.conversions != null) kpis.conversions = currentKpiSource.conversions;
|
|
131811
|
+
if (currentKpiSource.ctr != null) kpis.ctr = currentKpiSource.ctr;
|
|
131812
|
+
if (currentKpiSource.averageCpc != null) kpis.averageCpc = currentKpiSource.averageCpc;
|
|
131813
|
+
if (currentKpiSource.costPerConversion != null) {
|
|
131814
|
+
kpis.costPerConversion = currentKpiSource.costPerConversion;
|
|
131815
|
+
}
|
|
131816
|
+
}
|
|
131817
|
+
if (overview) {
|
|
131818
|
+
const avgDaily = num8(overview.averageDailyCost);
|
|
131819
|
+
if (avgDaily != null) kpis.averageDailyCost = avgDaily;
|
|
131820
|
+
const bal = num8(overview.balance);
|
|
131821
|
+
if (bal != null) kpis.balance = bal;
|
|
131822
|
+
const rem = num8(overview.remainingAccountBudget);
|
|
131823
|
+
if (rem != null) kpis.remainingAccountBudget = rem;
|
|
131824
|
+
const opt = num8(overview.optimizationScore);
|
|
131825
|
+
if (opt != null) kpis.optimizationScore = opt;
|
|
131826
|
+
}
|
|
131827
|
+
if (previousPeriod) {
|
|
131150
131828
|
kpis.previousPeriod = {
|
|
131151
131829
|
spend: num8(previousPeriod.spend),
|
|
131152
131830
|
impressions: num8(previousPeriod.impressions),
|
|
@@ -131158,35 +131836,33 @@ async function mergeBingAnalysisSnapshotIntoReport(payload, snapshotDir) {
|
|
|
131158
131836
|
};
|
|
131159
131837
|
}
|
|
131160
131838
|
const tables = { ...payload.tables ?? {} };
|
|
131161
|
-
|
|
131839
|
+
{
|
|
131162
131840
|
const rows = mapDeviceRows(sectionMap.get("device"));
|
|
131163
131841
|
if (rows.length > 0) tables.devices = rows;
|
|
131164
131842
|
}
|
|
131165
|
-
|
|
131843
|
+
{
|
|
131166
131844
|
const rows = mapGeographicRows(sectionMap.get("geographic"));
|
|
131167
131845
|
if (rows.length > 0) tables.geographic = rows;
|
|
131168
131846
|
}
|
|
131169
131847
|
const audienceMapped = mapAudienceRows(sectionMap.get("audience-merged"));
|
|
131170
|
-
if (
|
|
131171
|
-
|
|
131172
|
-
}
|
|
131173
|
-
if (!tables.audienceGender && audienceMapped.audienceGender.length > 0) {
|
|
131848
|
+
if (audienceMapped.audienceAge.length > 0) tables.audienceAge = audienceMapped.audienceAge;
|
|
131849
|
+
if (audienceMapped.audienceGender.length > 0) {
|
|
131174
131850
|
tables.audienceGender = audienceMapped.audienceGender;
|
|
131175
131851
|
}
|
|
131176
|
-
if (
|
|
131177
|
-
|
|
131852
|
+
if (campaignRows2.length > 0) tables.campaigns = campaignRows2;
|
|
131853
|
+
{
|
|
131178
131854
|
const rows = mapAdGroupRows(sectionMap.get("ad-groups"));
|
|
131179
131855
|
if (rows.length > 0) tables.adGroups = rows;
|
|
131180
131856
|
}
|
|
131181
|
-
|
|
131857
|
+
{
|
|
131182
131858
|
const rows = mapAdRows(sectionMap.get("ads"));
|
|
131183
131859
|
if (rows.length > 0) tables.ads = rows;
|
|
131184
131860
|
}
|
|
131185
|
-
|
|
131861
|
+
{
|
|
131186
131862
|
const rows = mapKeywordRows(sectionMap.get("keywords"));
|
|
131187
131863
|
if (rows.length > 0) tables.keywords = rows;
|
|
131188
131864
|
}
|
|
131189
|
-
|
|
131865
|
+
{
|
|
131190
131866
|
const rows = mapSearchTermRows(sectionMap.get("search-terms"));
|
|
131191
131867
|
if (rows.length > 0) tables.searchTerms = rows;
|
|
131192
131868
|
}
|
|
@@ -131666,7 +132342,7 @@ init_auth();
|
|
|
131666
132342
|
import * as path39 from "path";
|
|
131667
132343
|
import { performance as performance7 } from "perf_hooks";
|
|
131668
132344
|
init_version();
|
|
131669
|
-
async function
|
|
132345
|
+
async function runWithConcurrency5(tasks, concurrency) {
|
|
131670
132346
|
const results = new Array(tasks.length);
|
|
131671
132347
|
let cursor = 0;
|
|
131672
132348
|
const workers = new Array(Math.max(1, Math.min(concurrency, tasks.length))).fill(0).map(async () => {
|
|
@@ -131735,7 +132411,7 @@ async function runAllTikTokSections(opts) {
|
|
|
131735
132411
|
}
|
|
131736
132412
|
}
|
|
131737
132413
|
);
|
|
131738
|
-
const results = await
|
|
132414
|
+
const results = await runWithConcurrency5(tasks, concurrency);
|
|
131739
132415
|
const succeeded = results.filter((r) => r.ok).length;
|
|
131740
132416
|
const failed = results.length - succeeded;
|
|
131741
132417
|
const absoluteSnapshotDir = path39.resolve(opts.jsonOut);
|
|
@@ -131963,15 +132639,17 @@ async function mergeTikTokAnalysisSnapshotIntoReport(payload, snapshotDir) {
|
|
|
131963
132639
|
costPerConversion: num9(currentPeriod.costPerConversion)
|
|
131964
132640
|
} : null);
|
|
131965
132641
|
if (currentKpiSource) {
|
|
131966
|
-
if (
|
|
131967
|
-
if (
|
|
131968
|
-
if (
|
|
131969
|
-
if (
|
|
131970
|
-
if (
|
|
131971
|
-
if (
|
|
131972
|
-
if (
|
|
131973
|
-
|
|
131974
|
-
|
|
132642
|
+
if (currentKpiSource.spend != null) kpis.spend = currentKpiSource.spend;
|
|
132643
|
+
if (currentKpiSource.impressions != null) kpis.impressions = currentKpiSource.impressions;
|
|
132644
|
+
if (currentKpiSource.clicks != null) kpis.clicks = currentKpiSource.clicks;
|
|
132645
|
+
if (currentKpiSource.conversions != null) kpis.conversions = currentKpiSource.conversions;
|
|
132646
|
+
if (currentKpiSource.ctr != null) kpis.ctr = currentKpiSource.ctr;
|
|
132647
|
+
if (currentKpiSource.averageCpc != null) kpis.averageCpc = currentKpiSource.averageCpc;
|
|
132648
|
+
if (currentKpiSource.costPerConversion != null) {
|
|
132649
|
+
kpis.costPerConversion = currentKpiSource.costPerConversion;
|
|
132650
|
+
}
|
|
132651
|
+
}
|
|
132652
|
+
if (previousPeriod) {
|
|
131975
132653
|
kpis.previousPeriod = {
|
|
131976
132654
|
spend: num9(previousPeriod.spend),
|
|
131977
132655
|
impressions: num9(previousPeriod.impressions),
|
|
@@ -131983,29 +132661,29 @@ async function mergeTikTokAnalysisSnapshotIntoReport(payload, snapshotDir) {
|
|
|
131983
132661
|
};
|
|
131984
132662
|
}
|
|
131985
132663
|
const tables = { ...payload.tables ?? {} };
|
|
131986
|
-
if (
|
|
131987
|
-
|
|
132664
|
+
if (camps.length > 0) tables.campaigns = mapCampaignTable(camps);
|
|
132665
|
+
{
|
|
131988
132666
|
const rows = mapAdGroupTable(sectionMap.get("ad-groups"));
|
|
131989
132667
|
if (rows.length > 0) tables.adGroups = rows;
|
|
131990
132668
|
}
|
|
131991
|
-
|
|
132669
|
+
{
|
|
131992
132670
|
const rows = mapAdTable(sectionMap.get("ads"));
|
|
131993
132671
|
if (rows.length > 0) tables.ads = rows;
|
|
131994
132672
|
}
|
|
131995
|
-
|
|
132673
|
+
{
|
|
131996
132674
|
const rows = mapVideoTable(sectionMap.get("videos"));
|
|
131997
132675
|
if (rows.length > 0) tables.videos = rows;
|
|
131998
132676
|
}
|
|
131999
132677
|
const fromMerged = mapAudienceMerged(sectionMap.get("audience-merged"));
|
|
132000
|
-
|
|
132678
|
+
{
|
|
132001
132679
|
const rows = fromMerged.audienceGender.length > 0 ? fromMerged.audienceGender : mapAudienceList(sectionMap.get("audience-gender"), "gender", "gender");
|
|
132002
132680
|
if (rows.length > 0) tables.audienceGender = rows;
|
|
132003
132681
|
}
|
|
132004
|
-
|
|
132682
|
+
{
|
|
132005
132683
|
const rows = fromMerged.audienceAge.length > 0 ? fromMerged.audienceAge : mapAudienceList(sectionMap.get("audience-age"), "age", "ageRange");
|
|
132006
132684
|
if (rows.length > 0) tables.audienceAge = rows;
|
|
132007
132685
|
}
|
|
132008
|
-
|
|
132686
|
+
{
|
|
132009
132687
|
const rows = fromMerged.audienceInterest.length > 0 ? fromMerged.audienceInterest : mapAudienceList(
|
|
132010
132688
|
sectionMap.get("audience-interest-category"),
|
|
132011
132689
|
"interest_category",
|
|
@@ -132332,11 +133010,29 @@ function jsonOutMissingPath(args) {
|
|
|
132332
133010
|
}
|
|
132333
133011
|
return false;
|
|
132334
133012
|
}
|
|
133013
|
+
function adSubcommandHasMediaFlag(args) {
|
|
133014
|
+
if (args[0] !== "ad") return false;
|
|
133015
|
+
const sub = args[1];
|
|
133016
|
+
if (!sub || sub.startsWith("-")) return false;
|
|
133017
|
+
for (let i = 2; i < args.length; i++) {
|
|
133018
|
+
const a = args[i];
|
|
133019
|
+
if (a === "-m" || a === "--media" || a.startsWith("--media=")) return true;
|
|
133020
|
+
}
|
|
133021
|
+
return false;
|
|
133022
|
+
}
|
|
133023
|
+
function hasDateFromTo(args) {
|
|
133024
|
+
return args.some(
|
|
133025
|
+
(a) => a === "--date-from" || a === "--date-to" || a.startsWith("--date-from=") || a.startsWith("--date-to=")
|
|
133026
|
+
);
|
|
133027
|
+
}
|
|
132335
133028
|
function agentCompatFatalMessage(argv) {
|
|
132336
133029
|
const args = argv.slice(2);
|
|
132337
133030
|
if (args.includes("--period") || args.some((a) => a.startsWith("--period="))) {
|
|
132338
133031
|
return "\n\u274C \u6CA1\u6709 --period \u9009\u9879\u3002\u8BF7\u7528 --start/--end\uFF08\u6216\u517C\u5BB9\u522B\u540D --start-date/--end-date\uFF09\u6307\u5B9A YYYY-MM-DD \u533A\u95F4\u3002\n";
|
|
132339
133032
|
}
|
|
133033
|
+
if (hasDateFromTo(args)) {
|
|
133034
|
+
return "\n\u274C \u6CA1\u6709 `--date-from` / `--date-to`\u3002\u8BF7\u7528 `--start` / `--end`\uFF08YYYY-MM-DD\uFF09\u3002\n \u5355\u65E5\u9884\u7B97/\u7A7A\u8017\u7194\u65AD\u8BF7\u7528\uFF1Asiluzan-tso guard budget-circuit|zero-conv -m Google --date <\u5F53\u65E5> --json-out <dir>\n";
|
|
133035
|
+
}
|
|
132340
133036
|
if (jsonOutMissingPath(args)) {
|
|
132341
133037
|
return "\n\u274C `--json-out` \u5FC5\u987B\u5E26\u8DEF\u5F84\uFF08\u76EE\u5F55\u6216 *.json \u6587\u4EF6\uFF09\uFF0CJSON \u843D\u76D8\u800C\u975E\u6253\u5370\u5230 stdout\uFF0C\u4E0D\u80FD\u7BA1\u9053\u7ED9 python/jq\u3002\n \u793A\u4F8B\uFF1Asiluzan-tso ad campaigns -a <id> --json-out ./snap\n \u7136\u540E\u7528 node/python \u8BFB ./snap \u4E0B writtenFiles\uFF08\u89C1 stdout \u6458\u8981\u7684 absoluteSnapshotDir\uFF09\u3002\n";
|
|
132342
133038
|
}
|
|
@@ -132344,6 +133040,12 @@ function agentCompatFatalMessage(argv) {
|
|
|
132344
133040
|
if (cmd === "google-ads") {
|
|
132345
133041
|
return "\n\u274C \u6CA1\u6709 `google-ads` \u547D\u540D\u7A7A\u95F4\u3002\u8BF7\u6539\u7528\uFF1A\n - \u8D26\u6237\u5206\u6790\u62C9\u6570\uFF1Asiluzan-tso google-analysis \u2026\n - \u5E7F\u544A\u7CFB\u5217/\u7EC4/\u521B\u610F/\u5173\u952E\u8BCD\uFF1Asiluzan-tso ad campaigns|groups|list|keywords \u2026\n \uFF08\u6CA1\u6709 campaign-list\uFF1B\u5217\u8868\u547D\u4EE4\u662F campaigns / groups / list / keywords\uFF09\n - \u5E7F\u544A\u8BCA\u65AD\u62A5\u544A\uFF1Asiluzan-tso google-ads-diagnosis \u2026\n";
|
|
132346
133042
|
}
|
|
133043
|
+
if (cmd === "adgroups" || cmd === "ad" && args[1] === "adgroups") {
|
|
133044
|
+
return "\n\u274C \u6CA1\u6709 `adgroups` \u547D\u4EE4\u3002\u8BF7\u7528\uFF1Asiluzan-tso ad groups -a <mediaCustomerId> --json-out <dir>\n";
|
|
133045
|
+
}
|
|
133046
|
+
if (adSubcommandHasMediaFlag(args)) {
|
|
133047
|
+
return "\n\u274C `ad \u2026` \u5B50\u547D\u4EE4\u6CA1\u6709 `-m/--media`\uFF0C\u8D26\u6237\u7528 `-a/--account <mediaCustomerId>`\u3002\n \u5355\u6237\uFF1Asiluzan-tso ad campaigns -a <id> --start <\u65E5> --end <\u65E5> --json-out ./snap\n \u5168\u6237\u5F53\u65E5\u8D85\u9884\u7B97/\u7A7A\u8017\u7194\u65AD\uFF1Asiluzan-tso guard budget-circuit|zero-conv -m Google --json-out ./snap\n";
|
|
133048
|
+
}
|
|
132347
133049
|
if (cmd === "list" && (args.length === 1 || args[1]?.startsWith("-"))) {
|
|
132348
133050
|
return "\n\u274C \u6CA1\u6709\u9876\u5C42 `list` \u547D\u4EE4\u3002\u67E5\u8BE2\u5E7F\u544A\u8D26\u6237\u5217\u8868\u8BF7\u7528\uFF1A\n siluzan-tso list-accounts [-m Google] [-k <id\u6216\u5173\u952E\u5B57>] --json-out <dir>\n";
|
|
132349
133051
|
}
|
|
@@ -132415,12 +133117,12 @@ var REGISTRARS = [
|
|
|
132415
133117
|
register11,
|
|
132416
133118
|
register12,
|
|
132417
133119
|
register13,
|
|
133120
|
+
register14,
|
|
132418
133121
|
registerGoogleAnalysisCommands,
|
|
132419
133122
|
registerGoogleAnalysisBatchCommands,
|
|
132420
133123
|
registerFacebookAnalysisCommands,
|
|
132421
133124
|
registerBingAnalysisCommands,
|
|
132422
133125
|
registerTikTokAnalysisCommands,
|
|
132423
|
-
register14,
|
|
132424
133126
|
register15,
|
|
132425
133127
|
register16,
|
|
132426
133128
|
register17,
|
|
@@ -132432,6 +133134,7 @@ var REGISTRARS = [
|
|
|
132432
133134
|
register23,
|
|
132433
133135
|
register24,
|
|
132434
133136
|
register25,
|
|
133137
|
+
register26,
|
|
132435
133138
|
registerWebsiteDiagnosisCommands,
|
|
132436
133139
|
registerMarketAnalysisCommands,
|
|
132437
133140
|
registerGoogleAdsDiagnosisCommands
|