siluzan-tso-cli 1.1.21-beta.2 → 1.1.21-beta.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1 -1
- package/dist/index.js +444 -261
- package/dist/skill/SKILL.md +2 -2
- package/dist/skill/_meta.json +2 -2
- package/dist/skill/assets/pmax-create-template.json +1 -1
- package/dist/skill/assets/pmax-create-template.md +10 -7
- package/dist/skill/references/account-analytics.md +1 -1
- package/dist/skill/references/hosted-automation-monitoring-json.md +1 -1
- package/dist/skill/references/pmax-api.md +6 -0
- package/dist/skill/scripts/install.ps1 +1 -1
- package/dist/skill/scripts/install.sh +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -51,7 +51,7 @@ siluzan-tso init -d /path/to/skills # 写入自定义目录
|
|
|
51
51
|
siluzan-tso init --force # 强制覆盖已存在文件
|
|
52
52
|
```
|
|
53
53
|
|
|
54
|
-
> **注意**:当前为测试版(1.1.21-beta.
|
|
54
|
+
> **注意**:当前为测试版(1.1.21-beta.4),供内部测试使用。正式发布后安装命令将改为 `npm install -g siluzan-tso-cli`。
|
|
55
55
|
|
|
56
56
|
| 助手 | 建议 `--ai` |
|
|
57
57
|
| ----------------------- | ------------------------------------ |
|
package/dist/index.js
CHANGED
|
@@ -12,8 +12,8 @@ var __commonJS = (cb, mod) => function __require() {
|
|
|
12
12
|
return mod || (0, cb[__getOwnPropNames(cb)[0]])((mod = { exports: {} }).exports, mod), mod.exports;
|
|
13
13
|
};
|
|
14
14
|
var __export = (target, all) => {
|
|
15
|
-
for (var
|
|
16
|
-
__defProp(target,
|
|
15
|
+
for (var name2 in all)
|
|
16
|
+
__defProp(target, name2, { get: all[name2], enumerable: true });
|
|
17
17
|
};
|
|
18
18
|
var __copyProps = (to, from, except, desc) => {
|
|
19
19
|
if (from && typeof from === "object" || typeof from === "function") {
|
|
@@ -103,11 +103,11 @@ var require_re = __commonJS({
|
|
|
103
103
|
}
|
|
104
104
|
return value;
|
|
105
105
|
};
|
|
106
|
-
var createToken = (
|
|
106
|
+
var createToken = (name2, value, isGlobal) => {
|
|
107
107
|
const safe = makeSafeRegex(value);
|
|
108
108
|
const index = R++;
|
|
109
|
-
debug(
|
|
110
|
-
t[
|
|
109
|
+
debug(name2, index, value);
|
|
110
|
+
t[name2] = index;
|
|
111
111
|
src[index] = value;
|
|
112
112
|
safeSrc[index] = safe;
|
|
113
113
|
re[index] = new RegExp(value, isGlobal ? "g" : void 0);
|
|
@@ -2776,8 +2776,8 @@ function addCalendarDaysToYmd(ymd, deltaDays) {
|
|
|
2776
2776
|
const anchor = new Date(Date.UTC(y, mo - 1, d, 4, 0, 0));
|
|
2777
2777
|
return formatBeijingYmd(new Date(anchor.getTime() + deltaDays * 864e5));
|
|
2778
2778
|
}
|
|
2779
|
-
function parseAuditLogFilename(
|
|
2780
|
-
const m = AUDIT_LOG_FILENAME_RE.exec(
|
|
2779
|
+
function parseAuditLogFilename(name2) {
|
|
2780
|
+
const m = AUDIT_LOG_FILENAME_RE.exec(name2);
|
|
2781
2781
|
if (!m) return null;
|
|
2782
2782
|
return { day: m[1], segment: m[2] ? Number.parseInt(m[2], 10) : 0 };
|
|
2783
2783
|
}
|
|
@@ -3809,8 +3809,8 @@ async function readCliManifestIfExists(absDir, idSuffix) {
|
|
|
3809
3809
|
if (idSuffix && candidates[0] !== CLI_SNAPSHOT_MANIFEST_FILE) {
|
|
3810
3810
|
candidates.push(CLI_SNAPSHOT_MANIFEST_FILE);
|
|
3811
3811
|
}
|
|
3812
|
-
for (const
|
|
3813
|
-
const manifestPath = path7.join(absDir,
|
|
3812
|
+
for (const name2 of candidates) {
|
|
3813
|
+
const manifestPath = path7.join(absDir, name2);
|
|
3814
3814
|
try {
|
|
3815
3815
|
const raw = await fs4.readFile(manifestPath, "utf8");
|
|
3816
3816
|
const parsed = JSON.parse(raw);
|
|
@@ -4637,8 +4637,8 @@ async function readManifestIfExists(absDir, accountId) {
|
|
|
4637
4637
|
if (suffixed !== LEGACY_MANIFEST_FILE) {
|
|
4638
4638
|
candidates.push(LEGACY_MANIFEST_FILE);
|
|
4639
4639
|
}
|
|
4640
|
-
for (const
|
|
4641
|
-
const manifestPath = path11.join(absDir,
|
|
4640
|
+
for (const name2 of candidates) {
|
|
4641
|
+
const manifestPath = path11.join(absDir, name2);
|
|
4642
4642
|
try {
|
|
4643
4643
|
const raw = await fs7.readFile(manifestPath, "utf8");
|
|
4644
4644
|
const parsed = JSON.parse(raw);
|
|
@@ -5066,6 +5066,20 @@ var init_normalize_money = __esm({
|
|
|
5066
5066
|
}
|
|
5067
5067
|
});
|
|
5068
5068
|
|
|
5069
|
+
// src/commands/google-analysis/normalize-overview.ts
|
|
5070
|
+
function normalizeOverviewPayload(payload) {
|
|
5071
|
+
const obj = payload;
|
|
5072
|
+
if (typeof obj.balance === "number" && obj.balance === 0) {
|
|
5073
|
+
delete obj.balance;
|
|
5074
|
+
}
|
|
5075
|
+
return payload;
|
|
5076
|
+
}
|
|
5077
|
+
var init_normalize_overview = __esm({
|
|
5078
|
+
"src/commands/google-analysis/normalize-overview.ts"() {
|
|
5079
|
+
"use strict";
|
|
5080
|
+
}
|
|
5081
|
+
});
|
|
5082
|
+
|
|
5069
5083
|
// src/commands/google-analysis/match-type-en2zh.json
|
|
5070
5084
|
var match_type_en2zh_default;
|
|
5071
5085
|
var init_match_type_en2zh = __esm({
|
|
@@ -100587,8 +100601,8 @@ function rowsFromAccountDailyReportsEnvelope(raw, mediaCustomerId) {
|
|
|
100587
100601
|
const list = block?.data;
|
|
100588
100602
|
return Array.isArray(list) ? list : [];
|
|
100589
100603
|
}
|
|
100590
|
-
async function fetchGoogleAnalysisSectionJson(config, fullPath, verbose,
|
|
100591
|
-
switch (
|
|
100604
|
+
async function fetchGoogleAnalysisSectionJson(config, fullPath, verbose, name2) {
|
|
100605
|
+
switch (name2) {
|
|
100592
100606
|
case "overview":
|
|
100593
100607
|
return fetchJson(config, fullPath, verbose);
|
|
100594
100608
|
case "keywords":
|
|
@@ -100632,7 +100646,7 @@ async function fetchGoogleAnalysisSectionJson(config, fullPath, verbose, name) {
|
|
|
100632
100646
|
case "campaign-types":
|
|
100633
100647
|
return fetchJson(config, fullPath, verbose);
|
|
100634
100648
|
default:
|
|
100635
|
-
return assertNever(
|
|
100649
|
+
return assertNever(name2, "google-analysis");
|
|
100636
100650
|
}
|
|
100637
100651
|
}
|
|
100638
100652
|
function endpointHintFrom(def) {
|
|
@@ -100731,13 +100745,13 @@ async function fetchSectionPayload(def, opts, config, id) {
|
|
|
100731
100745
|
!!opts.verbose,
|
|
100732
100746
|
def.name
|
|
100733
100747
|
);
|
|
100734
|
-
|
|
100735
|
-
|
|
100736
|
-
|
|
100737
|
-
|
|
100738
|
-
)
|
|
100739
|
-
|
|
100740
|
-
);
|
|
100748
|
+
let payload = stripLegacyGoogleFieldsIfV2Present(data);
|
|
100749
|
+
payload = normalizeRateScales(payload, def.name);
|
|
100750
|
+
payload = normalizeMoneyScales(payload, def.name);
|
|
100751
|
+
if (def.name === "overview") {
|
|
100752
|
+
payload = normalizeOverviewPayload(payload);
|
|
100753
|
+
}
|
|
100754
|
+
return annotateZhFields(payload, def.name);
|
|
100741
100755
|
}
|
|
100742
100756
|
function endpointHintForSection(def) {
|
|
100743
100757
|
if (def.name === "materials") return "CampaignAssetView+Videos";
|
|
@@ -100779,6 +100793,7 @@ var init_fetch = __esm({
|
|
|
100779
100793
|
init_sections();
|
|
100780
100794
|
init_normalize_rates();
|
|
100781
100795
|
init_normalize_money();
|
|
100796
|
+
init_normalize_overview();
|
|
100782
100797
|
init_translate_fields();
|
|
100783
100798
|
}
|
|
100784
100799
|
});
|
|
@@ -105684,8 +105699,8 @@ async function readReportManifestIfExists(absDir, accountId) {
|
|
|
105684
105699
|
if (accountId && candidates[0] !== REPORT_MANIFEST_FILE) {
|
|
105685
105700
|
candidates.push(REPORT_MANIFEST_FILE);
|
|
105686
105701
|
}
|
|
105687
|
-
for (const
|
|
105688
|
-
const manifestPath = path12.join(absDir,
|
|
105702
|
+
for (const name2 of candidates) {
|
|
105703
|
+
const manifestPath = path12.join(absDir, name2);
|
|
105689
105704
|
try {
|
|
105690
105705
|
const raw = await fs8.readFile(manifestPath, "utf8");
|
|
105691
105706
|
const parsed = JSON.parse(raw);
|
|
@@ -106265,11 +106280,11 @@ async function runReportPushHistory(opts) {
|
|
|
106265
106280
|
const setting = d?.["setting"] ?? {};
|
|
106266
106281
|
const mediaEx = d?.["mediaAccountEx"] ?? {};
|
|
106267
106282
|
const reportEx = d?.["accountReportEx"] ?? {};
|
|
106268
|
-
const
|
|
106283
|
+
const name2 = String(setting["name"] ?? "");
|
|
106269
106284
|
const acct = mediaEx["mediaCustomerId"] != null && mediaEx["mediaCustomerName"] != null ? `${mediaEx["mediaCustomerId"]}-${mediaEx["mediaCustomerName"]}` : "";
|
|
106270
106285
|
const cycle = reportEx["reportStartDate"] != null ? String(reportEx["reportStartDate"]).slice(0, 10) : "";
|
|
106271
106286
|
const rid = String(row["entityId"] ?? row["id"] ?? "");
|
|
106272
|
-
return { name, account: acct, cycle, id: rid };
|
|
106287
|
+
return { name: name2, account: acct, cycle, id: rid };
|
|
106273
106288
|
});
|
|
106274
106289
|
printCliTable(rows, [
|
|
106275
106290
|
{ key: "name", header: "\u63A8\u9001\u540D\u79F0" },
|
|
@@ -106698,8 +106713,8 @@ function registerReportTiktokCommands(reportCmd) {
|
|
|
106698
106713
|
).option("--verbose", "\u8BE6\u7EC6\u9519\u8BEF\u4FE1\u606F", false).action(async (opts) => {
|
|
106699
106714
|
await runReportTikTokOverview(opts);
|
|
106700
106715
|
});
|
|
106701
|
-
const bindTake = (
|
|
106702
|
-
const cmd = reportCmd.command(
|
|
106716
|
+
const bindTake = (name2, segmentLabel, runner) => {
|
|
106717
|
+
const cmd = reportCmd.command(name2).description(`${desc} \u2014 ${segmentLabel}`);
|
|
106703
106718
|
addDateAndFetchOptions(cmd);
|
|
106704
106719
|
cmd.action(async (opts) => {
|
|
106705
106720
|
await runner(opts);
|
|
@@ -107184,8 +107199,8 @@ function addBingDateOptions(c) {
|
|
|
107184
107199
|
}
|
|
107185
107200
|
function registerReportBingCommands(reportCmd) {
|
|
107186
107201
|
const desc = "Bing\uFF08BingV2\uFF09\u8D26\u6237\u5206\u6790\uFF08TSO reporting/media-account/BingV2/...\uFF1B\u65E5\u671F\u4E0D\u53EF\u542B\u4ECA\u5929/\u6628\u5929\uFF09";
|
|
107187
|
-
const bindSimple = (
|
|
107188
|
-
const cmd = reportCmd.command(
|
|
107202
|
+
const bindSimple = (name2, label, runner) => {
|
|
107203
|
+
const cmd = reportCmd.command(name2).description(`${desc} \u2014 ${label}`);
|
|
107189
107204
|
addBingDateOptions(cmd);
|
|
107190
107205
|
cmd.action(async (opts) => {
|
|
107191
107206
|
await runner(opts);
|
|
@@ -107205,8 +107220,8 @@ function registerReportBingCommands(reportCmd) {
|
|
|
107205
107220
|
).option("--verbose", "\u8BE6\u7EC6\u9519\u8BEF\u4FE1\u606F", false).action(async (opts) => {
|
|
107206
107221
|
await runReportBingAudienceMerged(opts);
|
|
107207
107222
|
});
|
|
107208
|
-
const bindLimit = (
|
|
107209
|
-
const cmd = reportCmd.command(
|
|
107223
|
+
const bindLimit = (name2, label, runner) => {
|
|
107224
|
+
const cmd = reportCmd.command(name2).description(`${desc} \u2014 ${label}`);
|
|
107210
107225
|
addBingDateOptions(cmd);
|
|
107211
107226
|
cmd.option(
|
|
107212
107227
|
"--limit <n>",
|
|
@@ -109307,10 +109322,10 @@ TikTok \u7EBF\u7D22\u8868\u5355\uFF08\u8D26\u6237 ${opts.account}\uFF0C\u7B2C 1
|
|
|
109307
109322
|
for (const lead of leads) {
|
|
109308
109323
|
const sys = lead.system_fields ?? {};
|
|
109309
109324
|
const custom = lead.custom_fields ?? {};
|
|
109310
|
-
const
|
|
109325
|
+
const name2 = custom["name"] ?? custom["full_name"] ?? "\u2014";
|
|
109311
109326
|
const email = custom["email"] ?? "\u2014";
|
|
109312
109327
|
const phone = custom["phone number"] ?? custom["phone"] ?? "\u2014";
|
|
109313
|
-
console.log(` [${sys.region ?? "\u2014"}] ${
|
|
109328
|
+
console.log(` [${sys.region ?? "\u2014"}] ${name2} ${email} ${phone}`);
|
|
109314
109329
|
console.log(
|
|
109315
109330
|
` \u8868\u5355: ${sys.form_name ?? "\u2014"} \u5E7F\u544A: ${sys.ad_name ?? "\u2014"} \u65F6\u95F4: ${sys.created_time ?? "\u2014"}`
|
|
109316
109331
|
);
|
|
@@ -109371,11 +109386,11 @@ Meta \u7EBF\u7D22\u8868\u5355\uFF08\u8D26\u6237 ${opts.account}\uFF0C\u7B2C 1 \u
|
|
|
109371
109386
|
if (f.name) acc[f.name] = (f.values ?? []).join(", ");
|
|
109372
109387
|
return acc;
|
|
109373
109388
|
}, {});
|
|
109374
|
-
const
|
|
109389
|
+
const name2 = fields["full_name"] ?? fields["name"] ?? "\u2014";
|
|
109375
109390
|
const email = fields["email"] ?? "\u2014";
|
|
109376
109391
|
const phone = fields["phone_number"] ?? fields["phone"] ?? "\u2014";
|
|
109377
109392
|
const formName = lead.form?.name ?? "\u2014";
|
|
109378
|
-
console.log(` ${
|
|
109393
|
+
console.log(` ${name2} ${email} ${phone}`);
|
|
109379
109394
|
console.log(` \u8868\u5355: ${formName} \u65F6\u95F4: ${lead.created_Time ?? "\u2014"}`);
|
|
109380
109395
|
console.log();
|
|
109381
109396
|
}
|
|
@@ -110842,13 +110857,13 @@ function validateCampaignCreateConfigCore(cfg) {
|
|
|
110842
110857
|
"campaign.TargetContentNetwork \u5FC5\u987B\u4E3A false\uFF08\u641C\u7D22\u4E13\u5C5E\u65B9\u6848\uFF1A\u7981\u6B62\u5C55\u793A\u5E7F\u544A\u7F51\u7EDC\uFF09"
|
|
110843
110858
|
);
|
|
110844
110859
|
}
|
|
110845
|
-
const
|
|
110846
|
-
if (typeof
|
|
110860
|
+
const name2 = campaign["Name"];
|
|
110861
|
+
if (typeof name2 !== "string" || !name2.trim()) {
|
|
110847
110862
|
pushErr2(errors, "campaign.Name \u4E0D\u80FD\u4E3A\u7A7A");
|
|
110848
|
-
} else if (cfg.name?.trim() && cfg.name.trim() !==
|
|
110863
|
+
} else if (cfg.name?.trim() && cfg.name.trim() !== name2.trim()) {
|
|
110849
110864
|
pushWarn2(
|
|
110850
110865
|
warnings,
|
|
110851
|
-
`\u5916\u5C42 name\uFF08${cfg.name}\uFF09\u4E0E campaign.Name\uFF08${
|
|
110866
|
+
`\u5916\u5C42 name\uFF08${cfg.name}\uFF09\u4E0E campaign.Name\uFF08${name2}\uFF09\u4E0D\u4E00\u81F4\uFF1B\u63D0\u4EA4\u65F6 campaignName \u4F18\u5148\u53D6\u5916\u5C42 name`
|
|
110852
110867
|
);
|
|
110853
110868
|
}
|
|
110854
110869
|
const budget = campaign["Budget"];
|
|
@@ -111375,8 +111390,8 @@ async function runAdCampaignCreate(opts) {
|
|
|
111375
111390
|
let adCount = 0;
|
|
111376
111391
|
const groupNames = [];
|
|
111377
111392
|
for (const g of adGroups) {
|
|
111378
|
-
const
|
|
111379
|
-
if (typeof
|
|
111393
|
+
const name2 = g["Name"];
|
|
111394
|
+
if (typeof name2 === "string" && name2) groupNames.push(name2);
|
|
111380
111395
|
const kws = g["KeywordsForBatchJob"] ?? [];
|
|
111381
111396
|
for (const block of kws) kwCount += block.KeywordText?.length ?? 0;
|
|
111382
111397
|
const ads = g["AdsForBatchJob"] ?? [];
|
|
@@ -112592,9 +112607,9 @@ async function runAdGeoSearch(opts) {
|
|
|
112592
112607
|
}
|
|
112593
112608
|
for (const item of items) {
|
|
112594
112609
|
const id = String(item["id"] ?? "");
|
|
112595
|
-
const
|
|
112610
|
+
const name2 = String(item["locationName"] ?? item["canonicalName"] ?? item["name"] ?? "");
|
|
112596
112611
|
const type = String(item["targetType"] ?? item["typeV2"] ?? "");
|
|
112597
|
-
console.log(` id:${id} ${
|
|
112612
|
+
console.log(` id:${id} ${name2} [${type}]`);
|
|
112598
112613
|
}
|
|
112599
112614
|
console.log();
|
|
112600
112615
|
}
|
|
@@ -112650,14 +112665,14 @@ async function runAdGeoList(opts) {
|
|
|
112650
112665
|
}
|
|
112651
112666
|
for (const item of items) {
|
|
112652
112667
|
const id = String(item["id"] ?? item["countryCriteriaId"] ?? "");
|
|
112653
|
-
const
|
|
112668
|
+
const name2 = String(
|
|
112654
112669
|
item["name"] ?? item["countryOrRegion"] ?? item["country"] ?? item["location"] ?? item["canonicalName"] ?? ""
|
|
112655
112670
|
);
|
|
112656
112671
|
const campaign = item["campaignName"] ? ` campaign:${item["campaignName"]}` : "";
|
|
112657
112672
|
const bid = item["bidModifier"] != null && typeof item["bidModifier"] === "number" ? ` \u51FA\u4EF7\u8C03\u6574:${formatBidModifierPercent(item["bidModifier"])}` : item["bidModifier"] != null ? ` \u51FA\u4EF7\u8C03\u6574:${item["bidModifier"]}` : "";
|
|
112658
112673
|
const spend = item["spend"] != null ? ` \u6D88\u8017:${item["spend"]}` : "";
|
|
112659
112674
|
const clicks = item["clicks"] != null ? ` \u70B9\u51FB:${item["clicks"]}` : "";
|
|
112660
|
-
console.log(` id:${id} ${
|
|
112675
|
+
console.log(` id:${id} ${name2}${campaign}${bid}${spend}${clicks}`);
|
|
112661
112676
|
}
|
|
112662
112677
|
console.log();
|
|
112663
112678
|
}
|
|
@@ -112772,12 +112787,12 @@ async function runAdGeoSetBid(opts) {
|
|
|
112772
112787
|
process.exit(1);
|
|
112773
112788
|
}
|
|
112774
112789
|
const row = rows.find((r) => String(r["id"] ?? "") === criterionId);
|
|
112775
|
-
const
|
|
112790
|
+
const name2 = String(
|
|
112776
112791
|
row?.["locationName"] ?? row?.["name"] ?? row?.["countryOrRegion"] ?? criterionId
|
|
112777
112792
|
);
|
|
112778
112793
|
console.log(
|
|
112779
112794
|
`
|
|
112780
|
-
\u2705 \u5DF2\u66F4\u65B0\u5730\u7406\u4F4D\u7F6E\u51FA\u4EF7\uFF08\u7CFB\u5217 ${opts.campaignId} / ${
|
|
112795
|
+
\u2705 \u5DF2\u66F4\u65B0\u5730\u7406\u4F4D\u7F6E\u51FA\u4EF7\uFF08\u7CFB\u5217 ${opts.campaignId} / ${name2} / id ${criterionId} \u2192 \u500D\u7387 ${opts.bidModifier}\uFF0C${formatBidModifierPercent(opts.bidModifier)}\uFF09
|
|
112781
112796
|
`
|
|
112782
112797
|
);
|
|
112783
112798
|
}
|
|
@@ -113744,8 +113759,11 @@ function nonEmptyStrings(list) {
|
|
|
113744
113759
|
}
|
|
113745
113760
|
function hasImageSource(cfg, kind) {
|
|
113746
113761
|
const paths = cfg.imagePaths;
|
|
113762
|
+
const assetIdKey = kind === "marketing" ? "marketingImageAssetId" : kind === "square" ? "squareMarketingImageAssetId" : "logoImageAssetId";
|
|
113747
113763
|
const base64Key = kind === "marketing" ? "marketingImageBase64" : kind === "square" ? "squareMarketingImageBase64" : "logoImageBase64";
|
|
113748
113764
|
const pathKey = kind;
|
|
113765
|
+
const assetId = cfg[assetIdKey];
|
|
113766
|
+
if (typeof assetId === "string" && assetId.trim().length > 0) return true;
|
|
113749
113767
|
const b64 = cfg[base64Key];
|
|
113750
113768
|
if (typeof b64 === "string" && b64.trim().length > 0) return true;
|
|
113751
113769
|
const p = paths?.[pathKey];
|
|
@@ -113822,13 +113840,22 @@ function runPmaxCreateValidation(cfg) {
|
|
|
113822
113840
|
pushErr3(errors, "businessName\uFF08\u5546\u5BB6\u540D\u79F0\uFF09\u5FC5\u586B");
|
|
113823
113841
|
}
|
|
113824
113842
|
if (!hasImageSource(cfg, "marketing")) {
|
|
113825
|
-
pushErr3(
|
|
113843
|
+
pushErr3(
|
|
113844
|
+
errors,
|
|
113845
|
+
"\u6A2A\u56FE\u5FC5\u586B\uFF1AimagePaths.marketing\uFF08\u63A8\u8350\uFF0C\u63D0\u4EA4\u65F6\u81EA\u52A8\u4E0A\u4F20\uFF09\u6216 marketingImageAssetId / marketingImageBase64"
|
|
113846
|
+
);
|
|
113826
113847
|
}
|
|
113827
113848
|
if (!hasImageSource(cfg, "square")) {
|
|
113828
|
-
pushErr3(
|
|
113849
|
+
pushErr3(
|
|
113850
|
+
errors,
|
|
113851
|
+
"\u65B9\u56FE\u5FC5\u586B\uFF1AimagePaths.square \u6216 squareMarketingImageAssetId / squareMarketingImageBase64"
|
|
113852
|
+
);
|
|
113829
113853
|
}
|
|
113830
113854
|
if (!hasImageSource(cfg, "logo")) {
|
|
113831
|
-
pushErr3(
|
|
113855
|
+
pushErr3(
|
|
113856
|
+
errors,
|
|
113857
|
+
"Logo \u5FC5\u586B\uFF1AimagePaths.logo \u6216 logoImageAssetId / logoImageBase64"
|
|
113858
|
+
);
|
|
113832
113859
|
}
|
|
113833
113860
|
const bidding = normalizeBidding(cfg.biddingStrategyTypeV2);
|
|
113834
113861
|
if (!bidding) {
|
|
@@ -114054,9 +114081,205 @@ async function runPmaxImageValidation(configFile, cfg) {
|
|
|
114054
114081
|
return { errors, warnings };
|
|
114055
114082
|
}
|
|
114056
114083
|
|
|
114057
|
-
// src/commands/ad/pmax-
|
|
114084
|
+
// src/commands/ad/pmax-image-resolve.ts
|
|
114085
|
+
import { basename as basename4, dirname as dirname8, isAbsolute as isAbsolute3, resolve as resolve7 } from "path";
|
|
114058
114086
|
import { readFileSync as readFileSync5 } from "fs";
|
|
114059
|
-
|
|
114087
|
+
|
|
114088
|
+
// src/commands/ad/pmax-urls.ts
|
|
114089
|
+
function pmaxChannelTypesUrl(googleApiUrl) {
|
|
114090
|
+
const base = googleApiUrl.replace(/\/$/, "");
|
|
114091
|
+
return `${base}/campaign/pmax/channel-types`;
|
|
114092
|
+
}
|
|
114093
|
+
function pmaxCampaignUrl(googleApiUrl, accountId, campaignId) {
|
|
114094
|
+
const base = googleApiUrl.replace(/\/$/, "");
|
|
114095
|
+
const path22 = `${base}/accounts/${accountId}/campaign/pmax`;
|
|
114096
|
+
return campaignId ? `${path22}/${campaignId}` : path22;
|
|
114097
|
+
}
|
|
114098
|
+
function pmaxAssetGroupUrl(googleApiUrl, accountId, assetGroupId, suffix) {
|
|
114099
|
+
const base = googleApiUrl.replace(/\/$/, "");
|
|
114100
|
+
const path22 = `${base}/accounts/${accountId}/campaign/pmax/asset-group/${assetGroupId}`;
|
|
114101
|
+
return suffix ? `${path22}/${suffix.replace(/^\//, "")}` : path22;
|
|
114102
|
+
}
|
|
114103
|
+
function pmaxCampaignAssetGroupUrl(googleApiUrl, accountId, campaignId) {
|
|
114104
|
+
const base = googleApiUrl.replace(/\/$/, "");
|
|
114105
|
+
return `${base}/accounts/${accountId}/campaign/pmax/${campaignId}/asset-group`;
|
|
114106
|
+
}
|
|
114107
|
+
function pmaxUserlistsUrl(googleApiUrl, accountId) {
|
|
114108
|
+
const base = googleApiUrl.replace(/\/$/, "");
|
|
114109
|
+
return `${base}/campaignmanagement/userlists/${accountId}`;
|
|
114110
|
+
}
|
|
114111
|
+
function pmaxAudiencesUrl(googleApiUrl, accountId, limit) {
|
|
114112
|
+
const base = googleApiUrl.replace(/\/$/, "");
|
|
114113
|
+
const url = `${base}/campaignmanagement/audiences/${accountId}`;
|
|
114114
|
+
if (limit != null && Number.isFinite(limit)) {
|
|
114115
|
+
return `${url}?limit=${Math.floor(limit)}`;
|
|
114116
|
+
}
|
|
114117
|
+
return url;
|
|
114118
|
+
}
|
|
114119
|
+
function pmaxImageAssetUrl(googleApiUrl, accountId, name2) {
|
|
114120
|
+
const base = googleApiUrl.replace(/\/$/, "");
|
|
114121
|
+
const url = `${base}/mediamanagement/imageasset/${accountId}`;
|
|
114122
|
+
if (name2?.trim()) {
|
|
114123
|
+
return `${url}?name=${encodeURIComponent(name2.trim())}`;
|
|
114124
|
+
}
|
|
114125
|
+
return url;
|
|
114126
|
+
}
|
|
114127
|
+
function pmaxAssetGroupReportUrl(googleApiUrl, accountId, query) {
|
|
114128
|
+
const base = googleApiUrl.replace(/\/$/, "");
|
|
114129
|
+
const qs = query.toString();
|
|
114130
|
+
return `${base}/reporting/media-account/${accountId}/pmax/asset-groups/reports${qs ? `?${qs}` : ""}`;
|
|
114131
|
+
}
|
|
114132
|
+
function pmaxGeoReportUrl(googleApiUrl, accountId, query) {
|
|
114133
|
+
const base = googleApiUrl.replace(/\/$/, "");
|
|
114134
|
+
const qs = query.toString();
|
|
114135
|
+
return `${base}/reporting/media-account/${accountId}/pmax/campaigns/reports/geo${qs ? `?${qs}` : ""}`;
|
|
114136
|
+
}
|
|
114137
|
+
|
|
114138
|
+
// src/commands/ad/pmax-image-resolve.ts
|
|
114139
|
+
function resolvePmaxImagePath(configFile, relOrAbs) {
|
|
114140
|
+
const trimmed = relOrAbs.trim();
|
|
114141
|
+
if (isAbsolute3(trimmed)) return trimmed;
|
|
114142
|
+
return resolve7(dirname8(configFile), trimmed);
|
|
114143
|
+
}
|
|
114144
|
+
var SLOT_META = {
|
|
114145
|
+
marketing: {
|
|
114146
|
+
assetIdKey: "marketingImageAssetId",
|
|
114147
|
+
base64Key: "marketingImageBase64",
|
|
114148
|
+
pathKey: "marketing",
|
|
114149
|
+
configAssetIdKey: "marketingImageAssetId",
|
|
114150
|
+
configBase64Key: "marketingImageBase64",
|
|
114151
|
+
label: "\u6A2A\u56FE (MARKETING_IMAGE)"
|
|
114152
|
+
},
|
|
114153
|
+
square: {
|
|
114154
|
+
assetIdKey: "squareMarketingImageAssetId",
|
|
114155
|
+
base64Key: "squareMarketingImageBase64",
|
|
114156
|
+
pathKey: "square",
|
|
114157
|
+
configAssetIdKey: "squareMarketingImageAssetId",
|
|
114158
|
+
configBase64Key: "squareMarketingImageBase64",
|
|
114159
|
+
label: "\u65B9\u56FE (SQUARE_MARKETING_IMAGE)"
|
|
114160
|
+
},
|
|
114161
|
+
logo: {
|
|
114162
|
+
assetIdKey: "logoImageAssetId",
|
|
114163
|
+
base64Key: "logoImageBase64",
|
|
114164
|
+
pathKey: "logo",
|
|
114165
|
+
configAssetIdKey: "logoImageAssetId",
|
|
114166
|
+
configBase64Key: "logoImageBase64",
|
|
114167
|
+
label: "Logo (LOGO)"
|
|
114168
|
+
}
|
|
114169
|
+
};
|
|
114170
|
+
function guessContentType(fileName) {
|
|
114171
|
+
const lower = fileName.toLowerCase();
|
|
114172
|
+
if (lower.endsWith(".png")) return "image/png";
|
|
114173
|
+
if (lower.endsWith(".jpg") || lower.endsWith(".jpeg")) return "image/jpeg";
|
|
114174
|
+
if (lower.endsWith(".gif")) return "image/gif";
|
|
114175
|
+
if (lower.endsWith(".webp")) return "image/webp";
|
|
114176
|
+
return "application/octet-stream";
|
|
114177
|
+
}
|
|
114178
|
+
function normalizePmaxAssetId(value) {
|
|
114179
|
+
if (value == null) return "";
|
|
114180
|
+
if (typeof value === "number" && Number.isFinite(value)) return String(Math.trunc(value));
|
|
114181
|
+
const s = String(value).trim();
|
|
114182
|
+
if (!s) return "";
|
|
114183
|
+
const m = s.match(/\/assets\/(\d+)\s*$/);
|
|
114184
|
+
return m ? m[1] : s;
|
|
114185
|
+
}
|
|
114186
|
+
async function uploadPmaxImageFile(config, googleApiUrl, accountId, absImagePath, name2, verbose) {
|
|
114187
|
+
const fileName = basename4(absImagePath);
|
|
114188
|
+
const assetName = (name2 ?? fileName).trim() || fileName;
|
|
114189
|
+
const fileBuffer = readFileSync5(absImagePath);
|
|
114190
|
+
const mimeType = guessContentType(fileName);
|
|
114191
|
+
const form = new FormData();
|
|
114192
|
+
form.append("file", new Blob([fileBuffer], { type: mimeType }), fileName);
|
|
114193
|
+
const url = pmaxImageAssetUrl(googleApiUrl, accountId, assetName);
|
|
114194
|
+
const headers = {
|
|
114195
|
+
"Accept-Language": "zh-CN",
|
|
114196
|
+
Datapermission: config.dataPermission ?? ""
|
|
114197
|
+
};
|
|
114198
|
+
if (config.apiKey) headers["x-api-key"] = config.apiKey;
|
|
114199
|
+
else headers.Authorization = `Bearer ${config.authToken}`;
|
|
114200
|
+
const res = await fetch(url, { method: "POST", headers, body: form });
|
|
114201
|
+
const text = await res.text();
|
|
114202
|
+
if (!res.ok) {
|
|
114203
|
+
const detail = verbose ? `\uFF1A${text.slice(0, 300)}` : "";
|
|
114204
|
+
throw new Error(`\u4E0A\u4F20\u56FE\u7247\u8D44\u4EA7 HTTP ${res.status}${detail}\uFF08${absImagePath}\uFF09`);
|
|
114205
|
+
}
|
|
114206
|
+
let data;
|
|
114207
|
+
try {
|
|
114208
|
+
data = JSON.parse(text);
|
|
114209
|
+
} catch {
|
|
114210
|
+
throw new Error(`\u4E0A\u4F20\u56FE\u7247\u8D44\u4EA7\u54CD\u5E94\u975E JSON\uFF08${absImagePath}\uFF09`);
|
|
114211
|
+
}
|
|
114212
|
+
const id = normalizePmaxAssetId(data["id"]);
|
|
114213
|
+
if (!id) {
|
|
114214
|
+
throw new Error(`\u4E0A\u4F20\u56FE\u7247\u8D44\u4EA7\u672A\u8FD4\u56DE id\uFF08${absImagePath}\uFF09`);
|
|
114215
|
+
}
|
|
114216
|
+
return id;
|
|
114217
|
+
}
|
|
114218
|
+
async function resolveOneSlot(configFile, cfg, kind, accountId, config, googleApiUrl, verbose) {
|
|
114219
|
+
const meta = SLOT_META[kind];
|
|
114220
|
+
const presetId = String(cfg[meta.configAssetIdKey] ?? "").trim();
|
|
114221
|
+
if (presetId) {
|
|
114222
|
+
return { [meta.assetIdKey]: normalizePmaxAssetId(presetId) };
|
|
114223
|
+
}
|
|
114224
|
+
const inlineB64 = String(cfg[meta.configBase64Key] ?? "").trim();
|
|
114225
|
+
const pathRel = cfg.imagePaths?.[meta.pathKey]?.trim();
|
|
114226
|
+
if (pathRel) {
|
|
114227
|
+
const abs = resolvePmaxImagePath(configFile, pathRel);
|
|
114228
|
+
const id = await uploadPmaxImageFile(
|
|
114229
|
+
config,
|
|
114230
|
+
googleApiUrl,
|
|
114231
|
+
accountId,
|
|
114232
|
+
abs,
|
|
114233
|
+
basename4(abs),
|
|
114234
|
+
verbose
|
|
114235
|
+
);
|
|
114236
|
+
return { [meta.assetIdKey]: id };
|
|
114237
|
+
}
|
|
114238
|
+
if (inlineB64) {
|
|
114239
|
+
return { [meta.base64Key]: inlineB64 };
|
|
114240
|
+
}
|
|
114241
|
+
throw new Error(
|
|
114242
|
+
`${meta.label} \u672A\u914D\u7F6E\uFF1A\u8BF7\u63D0\u4F9B imagePaths.${String(meta.pathKey)}\u3001${String(meta.configBase64Key)} \u6216 ${String(meta.configAssetIdKey)}`
|
|
114243
|
+
);
|
|
114244
|
+
}
|
|
114245
|
+
async function resolvePmaxImageSlots(configFile, cfg, accountId, config, googleApiUrl, opts) {
|
|
114246
|
+
const kinds = ["marketing", "square", "logo"];
|
|
114247
|
+
const willUpload = kinds.some((k) => {
|
|
114248
|
+
const meta = SLOT_META[k];
|
|
114249
|
+
if (String(cfg[meta.configAssetIdKey] ?? "").trim()) return false;
|
|
114250
|
+
if (String(cfg[meta.configBase64Key] ?? "").trim()) return false;
|
|
114251
|
+
return Boolean(cfg.imagePaths?.[meta.pathKey]?.trim());
|
|
114252
|
+
});
|
|
114253
|
+
if (willUpload && opts?.logUploads !== false) {
|
|
114254
|
+
console.log("\n\u{1F4E4} \u6B63\u5728\u4E0A\u4F20 PMax \u56FE\u7247\u7D20\u6750\uFF08imagePaths \u2192 assetId\uFF09\u2026");
|
|
114255
|
+
}
|
|
114256
|
+
const parts = await Promise.all(
|
|
114257
|
+
kinds.map(
|
|
114258
|
+
(kind) => resolveOneSlot(configFile, cfg, kind, accountId, config, googleApiUrl, opts?.verbose)
|
|
114259
|
+
)
|
|
114260
|
+
);
|
|
114261
|
+
const merged = {};
|
|
114262
|
+
for (const part of parts) Object.assign(merged, part);
|
|
114263
|
+
if (willUpload && opts?.logUploads !== false) {
|
|
114264
|
+
console.log(
|
|
114265
|
+
` \u6A2A\u56FE id\uFF1A${merged.marketingImageAssetId ?? "(base64)"} \u65B9\u56FE id\uFF1A${merged.squareMarketingImageAssetId ?? "(base64)"} Logo id\uFF1A${merged.logoImageAssetId ?? "(base64)"}`
|
|
114266
|
+
);
|
|
114267
|
+
}
|
|
114268
|
+
return merged;
|
|
114269
|
+
}
|
|
114270
|
+
function assertPmaxImageSlotsResolved(slots) {
|
|
114271
|
+
for (const kind of ["marketing", "square", "logo"]) {
|
|
114272
|
+
const meta = SLOT_META[kind];
|
|
114273
|
+
const id = slots[meta.assetIdKey];
|
|
114274
|
+
const b64 = slots[meta.base64Key];
|
|
114275
|
+
if (!id && !b64) {
|
|
114276
|
+
throw new Error(`${meta.label} \u89E3\u6790\u540E\u4E3A\u7A7A`);
|
|
114277
|
+
}
|
|
114278
|
+
}
|
|
114279
|
+
}
|
|
114280
|
+
|
|
114281
|
+
// src/commands/ad/pmax-load.ts
|
|
114282
|
+
import { readFileSync as readFileSync6 } from "fs";
|
|
114060
114283
|
function loadPmaxCreateConfig(configFile) {
|
|
114061
114284
|
const cfg = tryLoadPmaxCreateConfig(configFile);
|
|
114062
114285
|
if (!cfg) {
|
|
@@ -114070,49 +114293,20 @@ function loadPmaxCreateConfig(configFile) {
|
|
|
114070
114293
|
function tryLoadPmaxCreateConfig(configFile) {
|
|
114071
114294
|
let raw;
|
|
114072
114295
|
try {
|
|
114073
|
-
raw = JSON.parse(
|
|
114296
|
+
raw = JSON.parse(readFileSync6(configFile, "utf8"));
|
|
114074
114297
|
} catch {
|
|
114075
114298
|
return null;
|
|
114076
114299
|
}
|
|
114077
114300
|
return stripMetaKeys(raw);
|
|
114078
114301
|
}
|
|
114079
|
-
function resolveImagePath2(configFile, relOrAbs) {
|
|
114080
|
-
const trimmed = relOrAbs.trim();
|
|
114081
|
-
if (isAbsolute3(trimmed)) return trimmed;
|
|
114082
|
-
return resolve7(dirname8(configFile), trimmed);
|
|
114083
|
-
}
|
|
114084
|
-
function readImageBase64(configFile, pathOrUndefined) {
|
|
114085
|
-
if (!pathOrUndefined?.trim()) return void 0;
|
|
114086
|
-
const abs = resolveImagePath2(configFile, pathOrUndefined);
|
|
114087
|
-
try {
|
|
114088
|
-
return readFileSync5(abs).toString("base64");
|
|
114089
|
-
} catch (e) {
|
|
114090
|
-
const msg = e instanceof Error ? e.message : String(e);
|
|
114091
|
-
throw new Error(`\u8BFB\u53D6\u56FE\u7247\u5931\u8D25\uFF08${abs}\uFF09\uFF1A${msg}`);
|
|
114092
|
-
}
|
|
114093
|
-
}
|
|
114094
114302
|
function parseLocationLanguageIds(items) {
|
|
114095
114303
|
if (!items?.length) return void 0;
|
|
114096
114304
|
return items.map((item) => ({
|
|
114097
114305
|
id: Number(item.id)
|
|
114098
114306
|
}));
|
|
114099
114307
|
}
|
|
114100
|
-
function buildPmaxCreateApiBody(
|
|
114101
|
-
|
|
114102
|
-
const marketingImageBase64 = cfg.marketingImageBase64?.trim() || readImageBase64(configFile, paths?.marketing) || "";
|
|
114103
|
-
const squareMarketingImageBase64 = cfg.squareMarketingImageBase64?.trim() || readImageBase64(configFile, paths?.square) || "";
|
|
114104
|
-
const logoImageBase64 = cfg.logoImageBase64?.trim() || readImageBase64(configFile, paths?.logo) || "";
|
|
114105
|
-
if (!marketingImageBase64) {
|
|
114106
|
-
throw new Error("marketingImageBase64 \u4E3A\u7A7A\uFF08\u8BF7\u914D\u7F6E imagePaths.marketing \u6216 marketingImageBase64\uFF09");
|
|
114107
|
-
}
|
|
114108
|
-
if (!squareMarketingImageBase64) {
|
|
114109
|
-
throw new Error(
|
|
114110
|
-
"squareMarketingImageBase64 \u4E3A\u7A7A\uFF08\u8BF7\u914D\u7F6E imagePaths.square \u6216 squareMarketingImageBase64\uFF09"
|
|
114111
|
-
);
|
|
114112
|
-
}
|
|
114113
|
-
if (!logoImageBase64) {
|
|
114114
|
-
throw new Error("logoImageBase64 \u4E3A\u7A7A\uFF08\u8BF7\u914D\u7F6E imagePaths.logo \u6216 logoImageBase64\uFF09");
|
|
114115
|
-
}
|
|
114308
|
+
function buildPmaxCreateApiBody(cfg, imageSlots) {
|
|
114309
|
+
assertPmaxImageSlotsResolved(imageSlots);
|
|
114116
114310
|
const body = {
|
|
114117
114311
|
name: cfg.name.trim(),
|
|
114118
114312
|
budget: toCentAmount(Number(cfg.budget)),
|
|
@@ -114121,9 +114315,7 @@ function buildPmaxCreateApiBody(configFile, cfg) {
|
|
|
114121
114315
|
longHeadlines: cfg.longHeadlines.map((h) => h.trim()).filter(Boolean),
|
|
114122
114316
|
descriptions: cfg.descriptions.map((d) => d.trim()).filter(Boolean),
|
|
114123
114317
|
businessName: cfg.businessName.trim(),
|
|
114124
|
-
|
|
114125
|
-
squareMarketingImageBase64,
|
|
114126
|
-
logoImageBase64
|
|
114318
|
+
...imageSlots
|
|
114127
114319
|
};
|
|
114128
114320
|
if (cfg.budgetName?.trim()) body.budgetName = cfg.budgetName.trim();
|
|
114129
114321
|
if (cfg.assetGroupName?.trim()) body.assetGroupName = cfg.assetGroupName.trim();
|
|
@@ -114171,9 +114363,25 @@ async function runAdPmaxCreate(opts) {
|
|
|
114171
114363
|
const config = await ensureDataPermission(loadConfig(opts.token));
|
|
114172
114364
|
const googleApiUrl = requireGoogleApi(config);
|
|
114173
114365
|
const accountId = cfg.account.toString().trim();
|
|
114366
|
+
let imageSlots;
|
|
114367
|
+
try {
|
|
114368
|
+
imageSlots = await resolvePmaxImageSlots(
|
|
114369
|
+
opts.configFile,
|
|
114370
|
+
cfg,
|
|
114371
|
+
accountId,
|
|
114372
|
+
config,
|
|
114373
|
+
googleApiUrl,
|
|
114374
|
+
{ verbose: opts.verbose, logUploads: !opts.json }
|
|
114375
|
+
);
|
|
114376
|
+
} catch (err) {
|
|
114377
|
+
console.error(`
|
|
114378
|
+
\u274C \u56FE\u7247\u4E0A\u4F20/\u89E3\u6790\u5931\u8D25\uFF1A${err instanceof Error ? err.message : String(err)}
|
|
114379
|
+
`);
|
|
114380
|
+
process.exit(1);
|
|
114381
|
+
}
|
|
114174
114382
|
let body;
|
|
114175
114383
|
try {
|
|
114176
|
-
body = buildPmaxCreateApiBody(
|
|
114384
|
+
body = buildPmaxCreateApiBody(cfg, imageSlots);
|
|
114177
114385
|
} catch (err) {
|
|
114178
114386
|
console.error(`
|
|
114179
114387
|
\u274C \u6784\u5EFA\u8BF7\u6C42\u4F53\u5931\u8D25\uFF1A${err instanceof Error ? err.message : String(err)}
|
|
@@ -114272,18 +114480,18 @@ async function runAdPmaxValidate(opts) {
|
|
|
114272
114480
|
}
|
|
114273
114481
|
|
|
114274
114482
|
// src/commands/ad/pmax-mgmt.ts
|
|
114275
|
-
import {
|
|
114483
|
+
import { basename as basename5, isAbsolute as isAbsolute5 } from "path";
|
|
114276
114484
|
|
|
114277
114485
|
// src/commands/ad/pmax-shared.ts
|
|
114278
114486
|
init_auth();
|
|
114279
114487
|
init_cli_json_snapshot();
|
|
114280
|
-
import { readFileSync as
|
|
114488
|
+
import { readFileSync as readFileSync7 } from "fs";
|
|
114281
114489
|
import { dirname as dirname9, isAbsolute as isAbsolute4, resolve as resolve8 } from "path";
|
|
114282
114490
|
var PMAX_MONEY_KEYS = /* @__PURE__ */ new Set(["budget", "targetCpa_BidingAmount"]);
|
|
114283
114491
|
function loadPmaxJsonFile(configFile) {
|
|
114284
114492
|
let raw;
|
|
114285
114493
|
try {
|
|
114286
|
-
raw = JSON.parse(
|
|
114494
|
+
raw = JSON.parse(readFileSync7(configFile, "utf8"));
|
|
114287
114495
|
} catch (e) {
|
|
114288
114496
|
const msg = e instanceof Error ? e.message : String(e);
|
|
114289
114497
|
console.error(`
|
|
@@ -114293,21 +114501,11 @@ function loadPmaxJsonFile(configFile) {
|
|
|
114293
114501
|
}
|
|
114294
114502
|
return stripMetaKeys(raw);
|
|
114295
114503
|
}
|
|
114296
|
-
function
|
|
114504
|
+
function resolvePmaxImagePath2(configFile, relOrAbs) {
|
|
114297
114505
|
const trimmed = relOrAbs.trim();
|
|
114298
114506
|
if (isAbsolute4(trimmed)) return trimmed;
|
|
114299
114507
|
return resolve8(dirname9(configFile), trimmed);
|
|
114300
114508
|
}
|
|
114301
|
-
function readImageBase642(configFile, pathOrUndefined) {
|
|
114302
|
-
if (!pathOrUndefined?.trim()) return void 0;
|
|
114303
|
-
const abs = resolveImagePath3(configFile, pathOrUndefined);
|
|
114304
|
-
try {
|
|
114305
|
-
return readFileSync6(abs).toString("base64");
|
|
114306
|
-
} catch (e) {
|
|
114307
|
-
const msg = e instanceof Error ? e.message : String(e);
|
|
114308
|
-
throw new Error(`\u8BFB\u53D6\u56FE\u7247\u5931\u8D25\uFF08${abs}\uFF09\uFF1A${msg}`);
|
|
114309
|
-
}
|
|
114310
|
-
}
|
|
114311
114509
|
function convertPmaxMoneyInObject(body) {
|
|
114312
114510
|
const out = { ...body };
|
|
114313
114511
|
for (const key of PMAX_MONEY_KEYS) {
|
|
@@ -114318,22 +114516,8 @@ function convertPmaxMoneyInObject(body) {
|
|
|
114318
114516
|
}
|
|
114319
114517
|
return out;
|
|
114320
114518
|
}
|
|
114321
|
-
function buildPmaxAssetGroupApiBody(
|
|
114322
|
-
|
|
114323
|
-
const marketingImageBase64 = cfg.marketingImageBase64?.trim() || readImageBase642(configFile, paths?.marketing) || "";
|
|
114324
|
-
const squareMarketingImageBase64 = cfg.squareMarketingImageBase64?.trim() || readImageBase642(configFile, paths?.square) || "";
|
|
114325
|
-
const logoImageBase64 = cfg.logoImageBase64?.trim() || readImageBase642(configFile, paths?.logo) || "";
|
|
114326
|
-
if (!marketingImageBase64) {
|
|
114327
|
-
throw new Error("marketingImageBase64 \u4E3A\u7A7A\uFF08\u8BF7\u914D\u7F6E imagePaths.marketing \u6216 marketingImageBase64\uFF09");
|
|
114328
|
-
}
|
|
114329
|
-
if (!squareMarketingImageBase64) {
|
|
114330
|
-
throw new Error(
|
|
114331
|
-
"squareMarketingImageBase64 \u4E3A\u7A7A\uFF08\u8BF7\u914D\u7F6E imagePaths.square \u6216 squareMarketingImageBase64\uFF09"
|
|
114332
|
-
);
|
|
114333
|
-
}
|
|
114334
|
-
if (!logoImageBase64) {
|
|
114335
|
-
throw new Error("logoImageBase64 \u4E3A\u7A7A\uFF08\u8BF7\u914D\u7F6E imagePaths.logo \u6216 logoImageBase64\uFF09");
|
|
114336
|
-
}
|
|
114519
|
+
function buildPmaxAssetGroupApiBody(cfg, imageSlots) {
|
|
114520
|
+
assertPmaxImageSlotsResolved(imageSlots);
|
|
114337
114521
|
return {
|
|
114338
114522
|
name: cfg.name.trim(),
|
|
114339
114523
|
finalUrls: cfg.finalUrls.map((u) => u.trim()).filter(Boolean),
|
|
@@ -114341,26 +114525,34 @@ function buildPmaxAssetGroupApiBody(configFile, cfg) {
|
|
|
114341
114525
|
longHeadlines: cfg.longHeadlines.map((h) => h.trim()).filter(Boolean),
|
|
114342
114526
|
descriptions: cfg.descriptions.map((d) => d.trim()).filter(Boolean),
|
|
114343
114527
|
businessName: cfg.businessName.trim(),
|
|
114344
|
-
|
|
114345
|
-
squareMarketingImageBase64,
|
|
114346
|
-
logoImageBase64
|
|
114528
|
+
...imageSlots
|
|
114347
114529
|
};
|
|
114348
114530
|
}
|
|
114349
|
-
function
|
|
114531
|
+
async function processAssetsUpdateBodyWithUpload(configFile, body, accountId, config, googleApiUrl, verbose) {
|
|
114350
114532
|
const out = { ...body };
|
|
114351
114533
|
const links = out["assetsToLink"];
|
|
114352
114534
|
if (!Array.isArray(links)) return out;
|
|
114353
|
-
out["assetsToLink"] =
|
|
114354
|
-
|
|
114355
|
-
|
|
114356
|
-
|
|
114357
|
-
|
|
114358
|
-
|
|
114359
|
-
|
|
114360
|
-
|
|
114361
|
-
|
|
114362
|
-
|
|
114363
|
-
|
|
114535
|
+
out["assetsToLink"] = await Promise.all(
|
|
114536
|
+
links.map(async (item) => {
|
|
114537
|
+
if (!item || typeof item !== "object") return item;
|
|
114538
|
+
const row = { ...item };
|
|
114539
|
+
const imagePath = row["imagePath"];
|
|
114540
|
+
if (typeof imagePath === "string" && imagePath.trim()) {
|
|
114541
|
+
const abs = resolvePmaxImagePath2(configFile, imagePath);
|
|
114542
|
+
row["assetId"] = await uploadPmaxImageFile(
|
|
114543
|
+
config,
|
|
114544
|
+
googleApiUrl,
|
|
114545
|
+
accountId,
|
|
114546
|
+
abs,
|
|
114547
|
+
void 0,
|
|
114548
|
+
verbose
|
|
114549
|
+
);
|
|
114550
|
+
delete row["imagePath"];
|
|
114551
|
+
delete row["imageBase64"];
|
|
114552
|
+
}
|
|
114553
|
+
return row;
|
|
114554
|
+
})
|
|
114555
|
+
);
|
|
114364
114556
|
return out;
|
|
114365
114557
|
}
|
|
114366
114558
|
async function withGoogleApi(opts, fn) {
|
|
@@ -114391,56 +114583,6 @@ function requireAccountId(account, label = "-a, --account") {
|
|
|
114391
114583
|
return id;
|
|
114392
114584
|
}
|
|
114393
114585
|
|
|
114394
|
-
// src/commands/ad/pmax-urls.ts
|
|
114395
|
-
function pmaxChannelTypesUrl(googleApiUrl) {
|
|
114396
|
-
const base = googleApiUrl.replace(/\/$/, "");
|
|
114397
|
-
return `${base}/campaign/pmax/channel-types`;
|
|
114398
|
-
}
|
|
114399
|
-
function pmaxCampaignUrl(googleApiUrl, accountId, campaignId) {
|
|
114400
|
-
const base = googleApiUrl.replace(/\/$/, "");
|
|
114401
|
-
const path22 = `${base}/accounts/${accountId}/campaign/pmax`;
|
|
114402
|
-
return campaignId ? `${path22}/${campaignId}` : path22;
|
|
114403
|
-
}
|
|
114404
|
-
function pmaxAssetGroupUrl(googleApiUrl, accountId, assetGroupId, suffix) {
|
|
114405
|
-
const base = googleApiUrl.replace(/\/$/, "");
|
|
114406
|
-
const path22 = `${base}/accounts/${accountId}/campaign/pmax/asset-group/${assetGroupId}`;
|
|
114407
|
-
return suffix ? `${path22}/${suffix.replace(/^\//, "")}` : path22;
|
|
114408
|
-
}
|
|
114409
|
-
function pmaxCampaignAssetGroupUrl(googleApiUrl, accountId, campaignId) {
|
|
114410
|
-
const base = googleApiUrl.replace(/\/$/, "");
|
|
114411
|
-
return `${base}/accounts/${accountId}/campaign/pmax/${campaignId}/asset-group`;
|
|
114412
|
-
}
|
|
114413
|
-
function pmaxUserlistsUrl(googleApiUrl, accountId) {
|
|
114414
|
-
const base = googleApiUrl.replace(/\/$/, "");
|
|
114415
|
-
return `${base}/campaignmanagement/userlists/${accountId}`;
|
|
114416
|
-
}
|
|
114417
|
-
function pmaxAudiencesUrl(googleApiUrl, accountId, limit) {
|
|
114418
|
-
const base = googleApiUrl.replace(/\/$/, "");
|
|
114419
|
-
const url = `${base}/campaignmanagement/audiences/${accountId}`;
|
|
114420
|
-
if (limit != null && Number.isFinite(limit)) {
|
|
114421
|
-
return `${url}?limit=${Math.floor(limit)}`;
|
|
114422
|
-
}
|
|
114423
|
-
return url;
|
|
114424
|
-
}
|
|
114425
|
-
function pmaxImageAssetUrl(googleApiUrl, accountId, name) {
|
|
114426
|
-
const base = googleApiUrl.replace(/\/$/, "");
|
|
114427
|
-
const url = `${base}/mediamanagement/imageasset/${accountId}`;
|
|
114428
|
-
if (name?.trim()) {
|
|
114429
|
-
return `${url}?name=${encodeURIComponent(name.trim())}`;
|
|
114430
|
-
}
|
|
114431
|
-
return url;
|
|
114432
|
-
}
|
|
114433
|
-
function pmaxAssetGroupReportUrl(googleApiUrl, accountId, query) {
|
|
114434
|
-
const base = googleApiUrl.replace(/\/$/, "");
|
|
114435
|
-
const qs = query.toString();
|
|
114436
|
-
return `${base}/reporting/media-account/${accountId}/pmax/asset-groups/reports${qs ? `?${qs}` : ""}`;
|
|
114437
|
-
}
|
|
114438
|
-
function pmaxGeoReportUrl(googleApiUrl, accountId, query) {
|
|
114439
|
-
const base = googleApiUrl.replace(/\/$/, "");
|
|
114440
|
-
const qs = query.toString();
|
|
114441
|
-
return `${base}/reporting/media-account/${accountId}/pmax/campaigns/reports/geo${qs ? `?${qs}` : ""}`;
|
|
114442
|
-
}
|
|
114443
|
-
|
|
114444
114586
|
// src/commands/ad/pmax-mgmt.ts
|
|
114445
114587
|
function buildReportQuery(opts) {
|
|
114446
114588
|
const params = new URLSearchParams();
|
|
@@ -114598,27 +114740,49 @@ async function runAdPmaxAssetGroupCreate(opts) {
|
|
|
114598
114740
|
console.error("\n\u274C JSON \u987B\u5305\u542B campaignId\n");
|
|
114599
114741
|
process.exit(1);
|
|
114600
114742
|
}
|
|
114601
|
-
|
|
114602
|
-
|
|
114603
|
-
|
|
114604
|
-
|
|
114605
|
-
|
|
114606
|
-
|
|
114607
|
-
|
|
114608
|
-
|
|
114609
|
-
|
|
114610
|
-
|
|
114611
|
-
|
|
114612
|
-
|
|
114613
|
-
|
|
114614
|
-
|
|
114615
|
-
}
|
|
114616
|
-
|
|
114743
|
+
const assetFields = {
|
|
114744
|
+
name: String(cfg["name"] ?? ""),
|
|
114745
|
+
finalUrls: cfg["finalUrls"] ?? [],
|
|
114746
|
+
businessName: String(cfg["businessName"] ?? ""),
|
|
114747
|
+
headlines: cfg["headlines"] ?? [],
|
|
114748
|
+
longHeadlines: cfg["longHeadlines"] ?? [],
|
|
114749
|
+
descriptions: cfg["descriptions"] ?? [],
|
|
114750
|
+
imagePaths: cfg["imagePaths"],
|
|
114751
|
+
marketingImageAssetId: cfg["marketingImageAssetId"],
|
|
114752
|
+
squareMarketingImageAssetId: cfg["squareMarketingImageAssetId"],
|
|
114753
|
+
logoImageAssetId: cfg["logoImageAssetId"],
|
|
114754
|
+
marketingImageBase64: cfg["marketingImageBase64"],
|
|
114755
|
+
squareMarketingImageBase64: cfg["squareMarketingImageBase64"],
|
|
114756
|
+
logoImageBase64: cfg["logoImageBase64"]
|
|
114757
|
+
};
|
|
114758
|
+
await withGoogleApi(opts, async (config, googleApiUrl) => {
|
|
114759
|
+
let imageSlots;
|
|
114760
|
+
try {
|
|
114761
|
+
imageSlots = await resolvePmaxImageSlots(
|
|
114762
|
+
opts.configFile,
|
|
114763
|
+
assetFields,
|
|
114764
|
+
accountId,
|
|
114765
|
+
config,
|
|
114766
|
+
googleApiUrl,
|
|
114767
|
+
{ verbose: opts.verbose, logUploads: !opts.json }
|
|
114768
|
+
);
|
|
114769
|
+
} catch (err) {
|
|
114770
|
+
console.error(
|
|
114771
|
+
`
|
|
114772
|
+
\u274C \u56FE\u7247\u4E0A\u4F20/\u89E3\u6790\u5931\u8D25\uFF1A${err instanceof Error ? err.message : String(err)}
|
|
114773
|
+
`
|
|
114774
|
+
);
|
|
114775
|
+
process.exit(1);
|
|
114776
|
+
}
|
|
114777
|
+
let body;
|
|
114778
|
+
try {
|
|
114779
|
+
body = buildPmaxAssetGroupApiBody(assetFields, imageSlots);
|
|
114780
|
+
} catch (err) {
|
|
114781
|
+
console.error(`
|
|
114617
114782
|
\u274C \u6784\u5EFA\u8BF7\u6C42\u4F53\u5931\u8D25\uFF1A${err instanceof Error ? err.message : String(err)}
|
|
114618
114783
|
`);
|
|
114619
|
-
|
|
114620
|
-
|
|
114621
|
-
await withGoogleApi(opts, async (config, googleApiUrl) => {
|
|
114784
|
+
process.exit(1);
|
|
114785
|
+
}
|
|
114622
114786
|
const url = pmaxCampaignAssetGroupUrl(googleApiUrl, accountId, campaignId);
|
|
114623
114787
|
let data;
|
|
114624
114788
|
try {
|
|
@@ -114716,17 +114880,24 @@ async function runAdPmaxAssetsUpdate(opts) {
|
|
|
114716
114880
|
console.error("\n\u274C JSON \u987B\u5305\u542B campaignId\uFF08PUT assets body \u5FC5\u586B\uFF09\n");
|
|
114717
114881
|
process.exit(1);
|
|
114718
114882
|
}
|
|
114719
|
-
|
|
114720
|
-
|
|
114721
|
-
|
|
114722
|
-
|
|
114723
|
-
|
|
114724
|
-
|
|
114883
|
+
await withGoogleApi(opts, async (config, googleApiUrl) => {
|
|
114884
|
+
let body;
|
|
114885
|
+
try {
|
|
114886
|
+
const { account: _a, ...rest } = cfg;
|
|
114887
|
+
body = await processAssetsUpdateBodyWithUpload(
|
|
114888
|
+
opts.configFile,
|
|
114889
|
+
rest,
|
|
114890
|
+
accountId,
|
|
114891
|
+
config,
|
|
114892
|
+
googleApiUrl,
|
|
114893
|
+
opts.verbose
|
|
114894
|
+
);
|
|
114895
|
+
} catch (err) {
|
|
114896
|
+
console.error(`
|
|
114725
114897
|
\u274C \u5904\u7406 assets JSON \u5931\u8D25\uFF1A${err instanceof Error ? err.message : String(err)}
|
|
114726
114898
|
`);
|
|
114727
|
-
|
|
114728
|
-
|
|
114729
|
-
await withGoogleApi(opts, async (config, googleApiUrl) => {
|
|
114899
|
+
process.exit(1);
|
|
114900
|
+
}
|
|
114730
114901
|
const url = pmaxAssetGroupUrl(googleApiUrl, accountId, assetGroupId, "assets");
|
|
114731
114902
|
try {
|
|
114732
114903
|
await pmaxApiFetch(
|
|
@@ -114967,43 +115138,55 @@ PMax \u53D7\u4F17\u6570\u636E\u6E90\uFF08\u8D26\u6237 ${accountId}\uFF09
|
|
|
114967
115138
|
}
|
|
114968
115139
|
async function runAdPmaxImageUpload(opts) {
|
|
114969
115140
|
const accountId = requireAccountId(opts.account);
|
|
114970
|
-
let name;
|
|
114971
|
-
let base64String;
|
|
114972
|
-
if (opts.bodyFile) {
|
|
114973
|
-
const cfg = loadPmaxJsonFile(opts.bodyFile);
|
|
114974
|
-
name = String(cfg["name"] ?? opts.name ?? "cli-image").trim();
|
|
114975
|
-
base64String = String(cfg["base64String"] ?? cfg["Base64String"] ?? "").trim() || readImageBase642(opts.bodyFile, String(cfg["imagePath"] ?? "")) || "";
|
|
114976
|
-
} else {
|
|
114977
|
-
name = (opts.name ?? "cli-image").trim();
|
|
114978
|
-
try {
|
|
114979
|
-
base64String = readFileSync7(opts.imagePath).toString("base64");
|
|
114980
|
-
} catch (err) {
|
|
114981
|
-
console.error(
|
|
114982
|
-
`
|
|
114983
|
-
\u274C \u8BFB\u53D6\u56FE\u7247\u5931\u8D25\uFF1A${err instanceof Error ? err.message : String(err)}
|
|
114984
|
-
`
|
|
114985
|
-
);
|
|
114986
|
-
process.exit(1);
|
|
114987
|
-
}
|
|
114988
|
-
}
|
|
114989
|
-
if (!base64String) {
|
|
114990
|
-
console.error("\n\u274C \u7F3A\u5C11\u56FE\u7247 Base64\uFF08--image-path \u6216 body JSON\uFF09\n");
|
|
114991
|
-
process.exit(1);
|
|
114992
|
-
}
|
|
114993
115141
|
await withGoogleApi(opts, async (config, googleApiUrl) => {
|
|
114994
|
-
const url = pmaxImageAssetUrl(googleApiUrl, accountId, name);
|
|
114995
115142
|
let data;
|
|
114996
115143
|
try {
|
|
114997
|
-
|
|
114998
|
-
|
|
114999
|
-
|
|
115000
|
-
|
|
115001
|
-
|
|
115002
|
-
|
|
115003
|
-
|
|
115004
|
-
|
|
115005
|
-
|
|
115006
|
-
|
|
115144
|
+
if (opts.imagePath?.trim()) {
|
|
115145
|
+
const abs = isAbsolute5(opts.imagePath) ? opts.imagePath.trim() : resolvePmaxImagePath2(process.cwd(), opts.imagePath);
|
|
115146
|
+
const name2 = (opts.name ?? basename5(abs)).trim();
|
|
115147
|
+
const id = await uploadPmaxImageFile(
|
|
115148
|
+
config,
|
|
115149
|
+
googleApiUrl,
|
|
115150
|
+
accountId,
|
|
115151
|
+
abs,
|
|
115152
|
+
name2,
|
|
115153
|
+
opts.verbose
|
|
115154
|
+
);
|
|
115155
|
+
data = { id, name: name2 };
|
|
115156
|
+
} else if (opts.bodyFile) {
|
|
115157
|
+
const cfg = loadPmaxJsonFile(opts.bodyFile);
|
|
115158
|
+
const name2 = String(cfg["name"] ?? opts.name ?? "cli-image").trim();
|
|
115159
|
+
const imagePath = String(cfg["imagePath"] ?? "").trim();
|
|
115160
|
+
if (imagePath) {
|
|
115161
|
+
const abs = resolvePmaxImagePath2(opts.bodyFile, imagePath);
|
|
115162
|
+
const id = await uploadPmaxImageFile(
|
|
115163
|
+
config,
|
|
115164
|
+
googleApiUrl,
|
|
115165
|
+
accountId,
|
|
115166
|
+
abs,
|
|
115167
|
+
name2,
|
|
115168
|
+
opts.verbose
|
|
115169
|
+
);
|
|
115170
|
+
data = { id, name: name2 };
|
|
115171
|
+
} else {
|
|
115172
|
+
const base64String = String(cfg["base64String"] ?? cfg["Base64String"] ?? "").trim() || "";
|
|
115173
|
+
if (!base64String) {
|
|
115174
|
+
console.error("\n\u274C body JSON \u987B\u542B imagePath \u6216 base64String\n");
|
|
115175
|
+
process.exit(1);
|
|
115176
|
+
}
|
|
115177
|
+
const url = pmaxImageAssetUrl(googleApiUrl, accountId, name2);
|
|
115178
|
+
const raw = await pmaxApiFetch(
|
|
115179
|
+
url,
|
|
115180
|
+
config,
|
|
115181
|
+
{ method: "POST", body: JSON.stringify({ name: name2, base64String }) },
|
|
115182
|
+
opts.verbose
|
|
115183
|
+
);
|
|
115184
|
+
data = raw && typeof raw === "object" && !Array.isArray(raw) ? raw : { data: raw };
|
|
115185
|
+
}
|
|
115186
|
+
} else {
|
|
115187
|
+
console.error("\n\u274C \u8BF7\u6307\u5B9A --image-path \u6216 --body-file\n");
|
|
115188
|
+
process.exit(1);
|
|
115189
|
+
}
|
|
115007
115190
|
} catch (err) {
|
|
115008
115191
|
console.error(
|
|
115009
115192
|
`
|
|
@@ -115150,7 +115333,7 @@ async function runAdCampaignValidate(opts) {
|
|
|
115150
115333
|
|
|
115151
115334
|
// src/commands/ad/pmax-image-convert.ts
|
|
115152
115335
|
import { mkdirSync as mkdirSync3, writeFileSync as writeFileSync5, readFileSync as readFileSync8, existsSync as existsSync3 } from "fs";
|
|
115153
|
-
import { resolve as resolve9, dirname as dirname10, basename as
|
|
115336
|
+
import { resolve as resolve9, dirname as dirname10, basename as basename6, extname } from "path";
|
|
115154
115337
|
var SPECS = [
|
|
115155
115338
|
{ kind: "marketing", width: 1200, height: 628, fit: "cover", suffix: "_marketing" },
|
|
115156
115339
|
{ kind: "square", width: 1200, height: 1200, fit: "cover", suffix: "_square" },
|
|
@@ -115199,7 +115382,7 @@ async function runAdPmaxImageConvert(opts) {
|
|
|
115199
115382
|
const firstInput = input ?? inputMarketing ?? inputSquare ?? inputLogo;
|
|
115200
115383
|
const outputDir = opts.outputDir ? resolve9(opts.outputDir) : dirname10(resolve9(firstInput));
|
|
115201
115384
|
mkdirSync3(outputDir, { recursive: true });
|
|
115202
|
-
const prefix = opts.prefix ??
|
|
115385
|
+
const prefix = opts.prefix ?? basename6(firstInput, extname(firstInput));
|
|
115203
115386
|
const quality = opts.quality ?? 85;
|
|
115204
115387
|
const sharpMod = await loadSharp();
|
|
115205
115388
|
const sharpFn = (p) => sharpMod(p);
|
|
@@ -115662,7 +115845,7 @@ function register20(program2) {
|
|
|
115662
115845
|
}
|
|
115663
115846
|
);
|
|
115664
115847
|
adCmd.command("pmax-create").description(
|
|
115665
|
-
'\u65B0\u5EFA Performance Max \u5E7F\u544A\u7CFB\u5217\uFF08\u540C\u6B65 API\uFF1B\u4EC5\u652F\u6301 JSON \u914D\u7F6E\u6587\u4EF6\uFF09\n\n \u7528\u6CD5\uFF1A\n 1. \u590D\u5236 pmax-create-template.json\uFF0C\u5B57\u6BB5\u8BF4\u660E\u89C1 pmax-create-template.md\n 2. siluzan-tso ad geo search -a <accountId> -q "United States"\n 3. siluzan-tso ad pmax-validate --config-file ./pmax.json\n 4. siluzan-tso ad pmax-create --config-file ./pmax.json\n 5. siluzan-tso ad campaigns -a <accountId> --json # \u786E\u8BA4 PERFORMANCE_MAX'
|
|
115848
|
+
'\u65B0\u5EFA Performance Max \u5E7F\u544A\u7CFB\u5217\uFF08\u540C\u6B65 API\uFF1B\u4EC5\u652F\u6301 JSON \u914D\u7F6E\u6587\u4EF6\uFF09\n\n \u7528\u6CD5\uFF1A\n 1. \u590D\u5236 pmax-create-template.json\uFF0C\u5B57\u6BB5\u8BF4\u660E\u89C1 pmax-create-template.md\n 2. siluzan-tso ad geo search -a <accountId> -q "United States"\n 3. siluzan-tso ad pmax-validate --config-file ./pmax.json\n 4. siluzan-tso ad pmax-create --config-file ./pmax.json # imagePaths \u81EA\u52A8\u4E0A\u4F20\n 5. siluzan-tso ad campaigns -a <accountId> --json # \u786E\u8BA4 PERFORMANCE_MAX'
|
|
115666
115849
|
).requiredOption(
|
|
115667
115850
|
"--config-file <path>",
|
|
115668
115851
|
"JSON \u914D\u7F6E\u6587\u4EF6\uFF08\u6A21\u677F\u89C1 assets/siluzan-ads/assets/pmax-create-*.json|md\uFF09"
|
|
@@ -116780,9 +116963,9 @@ function splitQueryWhitespaceToKeywords(query) {
|
|
|
116780
116963
|
function ragItemDedupeKey(item) {
|
|
116781
116964
|
const id = String(item.id ?? "").trim();
|
|
116782
116965
|
if (id) return `id:${id}`;
|
|
116783
|
-
const
|
|
116966
|
+
const name2 = String(item.fields?.name ?? "").trim();
|
|
116784
116967
|
const content = String(item.fields?.content ?? "").slice(0, 160);
|
|
116785
|
-
return `fb:${
|
|
116968
|
+
return `fb:${name2}\0${content}`;
|
|
116786
116969
|
}
|
|
116787
116970
|
function mergeRagResultsByBestScore(batches) {
|
|
116788
116971
|
const best = /* @__PURE__ */ new Map();
|
|
@@ -116870,12 +117053,12 @@ function formatRagResultsMarkdown(query, items, subQueries) {
|
|
|
116870
117053
|
items.forEach((item, itemIndex) => {
|
|
116871
117054
|
const fields = item.fields;
|
|
116872
117055
|
if (!fields) return;
|
|
116873
|
-
const
|
|
117056
|
+
const name2 = fields.name ?? "\u672A\u77E5\u6587\u4EF6";
|
|
116874
117057
|
const content = fields.content ?? "";
|
|
116875
117058
|
const sourceUrl = fields.source_url ?? "";
|
|
116876
117059
|
const createTime = fields.create_time ?? "";
|
|
116877
117060
|
const usageCount = fields.usage_count ?? 0;
|
|
116878
|
-
parts.push(`### ${itemIndex + 1}. ${
|
|
117061
|
+
parts.push(`### ${itemIndex + 1}. ${name2}
|
|
116879
117062
|
`);
|
|
116880
117063
|
if (item.id) {
|
|
116881
117064
|
parts.push(`**\u7247\u6BB5 ID\uFF1A** \`${item.id}\`
|
|
@@ -118926,7 +119109,7 @@ init_cli_table();
|
|
|
118926
119109
|
async function uploadAttachment(filePath, apiBaseUrl, config, verbose) {
|
|
118927
119110
|
const fileName = path14.basename(filePath);
|
|
118928
119111
|
const fileBuffer = fs9.readFileSync(filePath);
|
|
118929
|
-
const mimeType =
|
|
119112
|
+
const mimeType = guessContentType2(fileName);
|
|
118930
119113
|
const form = new FormData();
|
|
118931
119114
|
form.append("file", new Blob([fileBuffer], { type: mimeType }), fileName);
|
|
118932
119115
|
const uploadUrl = `${apiBaseUrl}/command/attachment`;
|
|
@@ -118952,7 +119135,7 @@ async function uploadAttachment(filePath, apiBaseUrl, config, verbose) {
|
|
|
118952
119135
|
}
|
|
118953
119136
|
return { id: data.id, fileName, storageProvider: data.storageProvider ?? "StorageAccount" };
|
|
118954
119137
|
}
|
|
118955
|
-
function
|
|
119138
|
+
function guessContentType2(fileName) {
|
|
118956
119139
|
const ext = path14.extname(fileName).toLowerCase();
|
|
118957
119140
|
const map = {
|
|
118958
119141
|
".jpg": "image/jpeg",
|
|
@@ -119227,10 +119410,10 @@ async function runOpenAccountGoogleTimezones(opts) {
|
|
|
119227
119410
|
if (kw) {
|
|
119228
119411
|
rows = rows.filter((r) => {
|
|
119229
119412
|
const code = String(r.Code ?? "").toLowerCase();
|
|
119230
|
-
const
|
|
119413
|
+
const name2 = String(r.Name ?? "").toLowerCase();
|
|
119231
119414
|
const time = String(r.Time ?? "").toLowerCase();
|
|
119232
119415
|
const label = `(${r.Time ?? ""})${r.Name ?? ""}`.toLowerCase();
|
|
119233
|
-
return code.includes(kw) ||
|
|
119416
|
+
return code.includes(kw) || name2.includes(kw) || time.includes(kw) || label.includes(kw);
|
|
119234
119417
|
});
|
|
119235
119418
|
}
|
|
119236
119419
|
const n = rows.length;
|
|
@@ -119652,9 +119835,9 @@ async function runOpenAccountTikTokTimezones(opts) {
|
|
|
119652
119835
|
if (kw) {
|
|
119653
119836
|
rows = rows.filter((r) => {
|
|
119654
119837
|
const code = String(r.Code ?? "").toLowerCase();
|
|
119655
|
-
const
|
|
119838
|
+
const name2 = String(r.Name ?? "").toLowerCase();
|
|
119656
119839
|
const time = String(r.Time ?? "").toLowerCase();
|
|
119657
|
-
return code.includes(kw) ||
|
|
119840
|
+
return code.includes(kw) || name2.includes(kw) || time.includes(kw);
|
|
119658
119841
|
});
|
|
119659
119842
|
}
|
|
119660
119843
|
const n = rows.length;
|
|
@@ -119741,12 +119924,12 @@ TikTok \u884C\u4E1A\u5217\u8868\uFF08\u7B2C 1 \u9875\uFF0C\u672C\u9875 ${rows.le
|
|
|
119741
119924
|
}
|
|
119742
119925
|
for (const r of rows) {
|
|
119743
119926
|
const id = String(r.IndustryId ?? "");
|
|
119744
|
-
const
|
|
119927
|
+
const name2 = String(r.IndustryName ?? "");
|
|
119745
119928
|
const isTop = id.length >= 6 && id.slice(4, 6) === "01";
|
|
119746
119929
|
if (isTop) {
|
|
119747
|
-
console.log(` \u25B8 ${
|
|
119930
|
+
console.log(` \u25B8 ${name2} (${id})`);
|
|
119748
119931
|
} else {
|
|
119749
|
-
console.log(` ${
|
|
119932
|
+
console.log(` ${name2} (${id})`);
|
|
119750
119933
|
}
|
|
119751
119934
|
}
|
|
119752
119935
|
console.log();
|
package/dist/skill/SKILL.md
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: siluzan-tso
|
|
3
3
|
description: >-
|
|
4
|
-
Siluzan TSO 广告 skill(siluzan-tso-cli):Google/Bing/Yandex/TikTok/Kwai 账户开户与管理、Google Ads CRUD、数据分析、优化报告、智能预警、TikTok/Meta 线索、发票财务,并提供 RAG 知识库查询,google
|
|
4
|
+
Siluzan TSO 广告 skill(siluzan-tso-cli):Google/Bing/Yandex/TikTok/Kwai 账户开户与管理、Google Ads CRUD、数据分析、优化报告、智能预警、TikTok/Meta 线索、发票财务,并提供 RAG 知识库查询,google广告关键词推荐;当你发现你的上下文被压缩过后,你需要根据用户的需求,重新阅读本skill相关的文件,保证skill功能正常。
|
|
5
5
|
license: MIT
|
|
6
6
|
metadata:
|
|
7
7
|
requires: nodejs,siluzan-tso-cli
|
|
@@ -153,7 +153,7 @@ Windows 注意:部分 Agent 客户端通过 PowerShell / cmd 代执行命令
|
|
|
153
153
|
- **常用字段**:
|
|
154
154
|
- `ad campaigns --json/--json-out` → `budget`(元,与 `campaign-edit --budget` 同口径)
|
|
155
155
|
- `ad groups --json` → `maxCPCAmountYuan` / `targetCpaAmountYuan`(元)
|
|
156
|
-
- `google-analysis overview-*.json` →
|
|
156
|
+
- `google-analysis overview-*.json` → **余额只认 `remainingAccountBudget`**(`balance` 为 0 时 CLI 已剔除该字段)
|
|
157
157
|
- `google-analysis campaigns-*.json` → `budgetAmountYuan` / `campaignTargetCpaYuan` / `maximizeConversionsTargetCpaYuan` / `spend` / `averageCpc` / `costPerConversion`(均元)
|
|
158
158
|
- `keyword --json` → `averageCpc` / `lowTopOfPageBid` / `highTopOfPageBid` + 根级与每条 `bidAmountCurrency`(无 `-a` 为 USD;有 `-a` 为账户 `currencyCode`);`-a <mediaCustomerId>` 走账户级推荐接口;限定市场用 `keyword geo-list` + `--geo <id>`(**多 id = 汇总指标**;分市场须多次调用、每次一个 `--geo`,见 `references/keyword-planner-workflows.md`)
|
|
159
159
|
- **品牌名优先级**:(1) 用户明确提供 → (2) `list-accounts.mag.advertiserName` → (3) 用户提供网址 → 域名占位并标注 `[待确认品牌名]`。**严禁**把英文域名翻译为虚构中文品牌。
|
package/dist/skill/_meta.json
CHANGED
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
"agentPitfalls": [
|
|
7
7
|
"勿与 Search campaign-create-template.json 混用(PMax 走 POST .../campaign/pmax,非 batch-asyncs)",
|
|
8
8
|
"budget / targetCpa_BidingAmount 填主币种「元」,CLI 提交前 ×100 转 API 整型",
|
|
9
|
-
"
|
|
9
|
+
"三张图:只填 imagePaths(pmax-create 会自动上传为 assetId);勿把 Base64 写入 JSON",
|
|
10
10
|
"提交前:ad pmax-validate → 用户确认 → ad pmax-create"
|
|
11
11
|
]
|
|
12
12
|
},
|
|
@@ -14,7 +14,7 @@
|
|
|
14
14
|
|------|----------|
|
|
15
15
|
| 创建 PMax | `pmax-validate` → 用户确认 → `pmax-create` |
|
|
16
16
|
| 金额 | JSON 填**主币种「元」**;CLI 提交前 `budget`、`targetCpa_BidingAmount` ×100 |
|
|
17
|
-
| 图片 |
|
|
17
|
+
| 图片 | **只填 `imagePaths`** 指向本地 PNG/JPEG;`pmax-create` 会自动 multipart 上传并用 assetId 创建(勿把 Base64 提交进 Git) |
|
|
18
18
|
| 改已上线 PMax | `ad pmax-get` / `pmax-edit` / `pmax-assets-update` 等(见 `references/pmax-api.md`) |
|
|
19
19
|
| 列表复核 | `ad campaigns -a <id> --json`,`channelTypeV2` 应为 `PERFORMANCE_MAX` |
|
|
20
20
|
|
|
@@ -46,12 +46,15 @@ siluzan-tso ad campaigns -a <accountId> --json
|
|
|
46
46
|
| `headlines` | string[] | ✅ | ≥3 条标题 |
|
|
47
47
|
| `longHeadlines` | string[] | ✅ | ≥1 条长标题 |
|
|
48
48
|
| `descriptions` | string[] | ✅ | ≥2 条描述 |
|
|
49
|
-
| `imagePaths.marketing` | string | ✅* | 横图 1.91:1(建议 1200×628
|
|
50
|
-
| `imagePaths.square` | string | ✅* | 方图 1:1(建议 1200×1200
|
|
51
|
-
| `imagePaths.logo` | string | ✅* | Logo 1:1 |
|
|
52
|
-
| `
|
|
53
|
-
| `
|
|
54
|
-
| `
|
|
49
|
+
| `imagePaths.marketing` | string | ✅* | 横图 1.91:1(建议 1200×628)路径,相对配置文件目录;**提交时自动上传** |
|
|
50
|
+
| `imagePaths.square` | string | ✅* | 方图 1:1(建议 1200×1200);自动上传 |
|
|
51
|
+
| `imagePaths.logo` | string | ✅* | Logo 1:1;自动上传 |
|
|
52
|
+
| `marketingImageAssetId` | string | ✅* | 已上传横图 asset.id(与 path/base64 三选一) |
|
|
53
|
+
| `squareMarketingImageAssetId` | string | ✅* | 已上传方图 asset.id |
|
|
54
|
+
| `logoImageAssetId` | string | ✅* | 已上传 Logo asset.id |
|
|
55
|
+
| `marketingImageBase64` | string | ✅* | 内联 Base64(不推荐;无 path 时用) |
|
|
56
|
+
| `squareMarketingImageBase64` | string | ✅* | 内联方图 Base64 |
|
|
57
|
+
| `logoImageBase64` | string | ✅* | 内联 Logo Base64 |
|
|
55
58
|
| `targetedLocations` | `{ id }[]` | | 地理 criterion ID,如 `{ "id": "2840" }`(美国) |
|
|
56
59
|
| `targetedLanguages` | `{ id }[]` | | 语言 ID,如 `{ "id": 1000 }`(英语) |
|
|
57
60
|
| `biddingStrategyTypeV2` | string | | 默认 `MAXIMIZE_CONVERSIONS` |
|
|
@@ -84,7 +84,7 @@ CLI 出口的所有 JSON / 表格金额已统一为**元**,关键字段:
|
|
|
84
84
|
| `ad groups --json` | `maxCPCAmountYuan`、`targetCpaAmountYuan` |
|
|
85
85
|
| `google-analysis campaigns` 落盘 `campaigns-*.json` | `budgetAmountYuan`、`campaignTargetCpaYuan`、`maximizeConversionsTargetCpaYuan`;同行 `spend` / `averageCpc` / `costPerConversion` 也是元 |
|
|
86
86
|
| `keyword suggest --json` | `averageCpc`、`lowTopOfPageBid`、`highTopOfPageBid`;根级与每条 `bidAmountCurrency`(有 `-a` 为账户币;无 `-a` 为 USD) |
|
|
87
|
-
| `balance` 等账户余额接口 |
|
|
87
|
+
| `balance` 等账户余额接口 | 余额字段为`remainingAccountBudget`(元) |
|
|
88
88
|
|
|
89
89
|
旧字段 `budgetAmount`(分)、`maxCPCAmountDisplay`、`*Micros`(微元)**已不再落盘**,下游脚本无需做单位换算。金额保留 2 位小数,带货币代码(如 `¥50.00 CNY`、`$50.00 USD`),`currencyCode` 从响应读取,跨币种账户分表;细则见 `references/currency.md`。
|
|
90
90
|
|
|
@@ -43,7 +43,7 @@ siluzan-tso balance -m Google --accounts <mediaCustomerId> --json
|
|
|
43
43
|
mkdir -p ./snap-monitor && siluzan-tso google-analysis -a <mediaCustomerId> --sections overview --start <YYYY-MM-DD> --end <YYYY-MM-DD> --json-out ./snap-monitor
|
|
44
44
|
```
|
|
45
45
|
|
|
46
|
-
读 **`./snap-monitor/overview-<accountId>.json`**(具体路径见 stdout 摘要的 `writtenFiles[0]` 或 `manifest-<accountId>.json` 的 `artifacts`)。根对象常见字段:**`remainingAccountBudget
|
|
46
|
+
读 **`./snap-monitor/overview-<accountId>.json`**(具体路径见 stdout 摘要的 `writtenFiles[0]` 或 `manifest-<accountId>.json` 的 `artifacts`)。根对象常见字段:**`remainingAccountBudget`**(余额)、**`averageDailyCost`**、**`totalCost`**、**`activeDays`**、**`currencyCode`**、**`accountId`**(无 **`balance`**,网关为 0 时 CLI 已剔除);**`currentPeriod`** / **`previousPeriod`** 为对象块,内含 **`spend`**、`impressions`、`clicks`、`conversions` 等与 `references/account-analytics.md` 总览口径一致的指标(块内还可能出现 **`currencyCode`**)。
|
|
47
47
|
|
|
48
48
|
---
|
|
49
49
|
|
|
@@ -38,6 +38,12 @@
|
|
|
38
38
|
|
|
39
39
|
模板:`assets/pmax-create-template.json`、`pmax-asset-group-template.json`、`pmax-assets-update-template.json`、`pmax-signals-template.json`、`pmax-patch-campaign-template.json`。
|
|
40
40
|
|
|
41
|
+
### 图片(Agent 推荐)
|
|
42
|
+
|
|
43
|
+
1. JSON 中只配置 `imagePaths`(三张本地图路径)。
|
|
44
|
+
2. `ad pmax-validate` → 用户确认 → `ad pmax-create`:**CLI 自动** `POST /mediamanagement/imageasset/{accountId}`(multipart)×3,再以 `*ImageAssetId` 调用 `POST .../campaign/pmax`(创建 Body **不含** 大图 Base64)。
|
|
45
|
+
3. 单张预上传(可选):`ad pmax-image-upload -a <id> --image-path ./x.png`,将返回的 `id` 写入 `marketingImageAssetId` 等。
|
|
46
|
+
|
|
41
47
|
---
|
|
42
48
|
|
|
43
49
|
## 编辑流程(无整包 PUT)
|
|
@@ -9,7 +9,7 @@ $ErrorActionPreference = 'Stop'
|
|
|
9
9
|
# -- Package info (injected at build time) ------------------------------------
|
|
10
10
|
$PKG_NAME = 'siluzan-tso-cli'
|
|
11
11
|
# PKG_VERSION 锁定到与本脚本同批构建产物一致的版本,避免与 dist/skill 错位
|
|
12
|
-
$PKG_VERSION = '1.1.21-beta.
|
|
12
|
+
$PKG_VERSION = '1.1.21-beta.4'
|
|
13
13
|
$CLI_BIN = 'siluzan-tso'
|
|
14
14
|
$SKILL_LABEL = 'Siluzan TSO'
|
|
15
15
|
$INSTALL_CMD = 'npm install -g siluzan-tso-cli@beta'
|
|
@@ -9,7 +9,7 @@ set -euo pipefail
|
|
|
9
9
|
# -- Package info (injected at build time) ------------------------------------
|
|
10
10
|
readonly PKG_NAME="siluzan-tso-cli"
|
|
11
11
|
# PKG_VERSION 锁定到与本脚本同批构建产物一致的版本,避免与 dist/skill 错位
|
|
12
|
-
readonly PKG_VERSION="1.1.21-beta.
|
|
12
|
+
readonly PKG_VERSION="1.1.21-beta.4"
|
|
13
13
|
readonly CLI_BIN="siluzan-tso"
|
|
14
14
|
readonly SKILL_LABEL="Siluzan TSO"
|
|
15
15
|
readonly INSTALL_CMD="npm install -g siluzan-tso-cli@beta"
|