siluzan-tso-cli 1.1.21-beta.2 → 1.1.21-beta.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1 -1
- package/dist/index.js +422 -254
- 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/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.3),供内部测试使用。正式发布后安装命令将改为 `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);
|
|
@@ -100587,8 +100587,8 @@ function rowsFromAccountDailyReportsEnvelope(raw, mediaCustomerId) {
|
|
|
100587
100587
|
const list = block?.data;
|
|
100588
100588
|
return Array.isArray(list) ? list : [];
|
|
100589
100589
|
}
|
|
100590
|
-
async function fetchGoogleAnalysisSectionJson(config, fullPath, verbose,
|
|
100591
|
-
switch (
|
|
100590
|
+
async function fetchGoogleAnalysisSectionJson(config, fullPath, verbose, name2) {
|
|
100591
|
+
switch (name2) {
|
|
100592
100592
|
case "overview":
|
|
100593
100593
|
return fetchJson(config, fullPath, verbose);
|
|
100594
100594
|
case "keywords":
|
|
@@ -100632,7 +100632,7 @@ async function fetchGoogleAnalysisSectionJson(config, fullPath, verbose, name) {
|
|
|
100632
100632
|
case "campaign-types":
|
|
100633
100633
|
return fetchJson(config, fullPath, verbose);
|
|
100634
100634
|
default:
|
|
100635
|
-
return assertNever(
|
|
100635
|
+
return assertNever(name2, "google-analysis");
|
|
100636
100636
|
}
|
|
100637
100637
|
}
|
|
100638
100638
|
function endpointHintFrom(def) {
|
|
@@ -105684,8 +105684,8 @@ async function readReportManifestIfExists(absDir, accountId) {
|
|
|
105684
105684
|
if (accountId && candidates[0] !== REPORT_MANIFEST_FILE) {
|
|
105685
105685
|
candidates.push(REPORT_MANIFEST_FILE);
|
|
105686
105686
|
}
|
|
105687
|
-
for (const
|
|
105688
|
-
const manifestPath = path12.join(absDir,
|
|
105687
|
+
for (const name2 of candidates) {
|
|
105688
|
+
const manifestPath = path12.join(absDir, name2);
|
|
105689
105689
|
try {
|
|
105690
105690
|
const raw = await fs8.readFile(manifestPath, "utf8");
|
|
105691
105691
|
const parsed = JSON.parse(raw);
|
|
@@ -106265,11 +106265,11 @@ async function runReportPushHistory(opts) {
|
|
|
106265
106265
|
const setting = d?.["setting"] ?? {};
|
|
106266
106266
|
const mediaEx = d?.["mediaAccountEx"] ?? {};
|
|
106267
106267
|
const reportEx = d?.["accountReportEx"] ?? {};
|
|
106268
|
-
const
|
|
106268
|
+
const name2 = String(setting["name"] ?? "");
|
|
106269
106269
|
const acct = mediaEx["mediaCustomerId"] != null && mediaEx["mediaCustomerName"] != null ? `${mediaEx["mediaCustomerId"]}-${mediaEx["mediaCustomerName"]}` : "";
|
|
106270
106270
|
const cycle = reportEx["reportStartDate"] != null ? String(reportEx["reportStartDate"]).slice(0, 10) : "";
|
|
106271
106271
|
const rid = String(row["entityId"] ?? row["id"] ?? "");
|
|
106272
|
-
return { name, account: acct, cycle, id: rid };
|
|
106272
|
+
return { name: name2, account: acct, cycle, id: rid };
|
|
106273
106273
|
});
|
|
106274
106274
|
printCliTable(rows, [
|
|
106275
106275
|
{ key: "name", header: "\u63A8\u9001\u540D\u79F0" },
|
|
@@ -106698,8 +106698,8 @@ function registerReportTiktokCommands(reportCmd) {
|
|
|
106698
106698
|
).option("--verbose", "\u8BE6\u7EC6\u9519\u8BEF\u4FE1\u606F", false).action(async (opts) => {
|
|
106699
106699
|
await runReportTikTokOverview(opts);
|
|
106700
106700
|
});
|
|
106701
|
-
const bindTake = (
|
|
106702
|
-
const cmd = reportCmd.command(
|
|
106701
|
+
const bindTake = (name2, segmentLabel, runner) => {
|
|
106702
|
+
const cmd = reportCmd.command(name2).description(`${desc} \u2014 ${segmentLabel}`);
|
|
106703
106703
|
addDateAndFetchOptions(cmd);
|
|
106704
106704
|
cmd.action(async (opts) => {
|
|
106705
106705
|
await runner(opts);
|
|
@@ -107184,8 +107184,8 @@ function addBingDateOptions(c) {
|
|
|
107184
107184
|
}
|
|
107185
107185
|
function registerReportBingCommands(reportCmd) {
|
|
107186
107186
|
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(
|
|
107187
|
+
const bindSimple = (name2, label, runner) => {
|
|
107188
|
+
const cmd = reportCmd.command(name2).description(`${desc} \u2014 ${label}`);
|
|
107189
107189
|
addBingDateOptions(cmd);
|
|
107190
107190
|
cmd.action(async (opts) => {
|
|
107191
107191
|
await runner(opts);
|
|
@@ -107205,8 +107205,8 @@ function registerReportBingCommands(reportCmd) {
|
|
|
107205
107205
|
).option("--verbose", "\u8BE6\u7EC6\u9519\u8BEF\u4FE1\u606F", false).action(async (opts) => {
|
|
107206
107206
|
await runReportBingAudienceMerged(opts);
|
|
107207
107207
|
});
|
|
107208
|
-
const bindLimit = (
|
|
107209
|
-
const cmd = reportCmd.command(
|
|
107208
|
+
const bindLimit = (name2, label, runner) => {
|
|
107209
|
+
const cmd = reportCmd.command(name2).description(`${desc} \u2014 ${label}`);
|
|
107210
107210
|
addBingDateOptions(cmd);
|
|
107211
107211
|
cmd.option(
|
|
107212
107212
|
"--limit <n>",
|
|
@@ -109307,10 +109307,10 @@ TikTok \u7EBF\u7D22\u8868\u5355\uFF08\u8D26\u6237 ${opts.account}\uFF0C\u7B2C 1
|
|
|
109307
109307
|
for (const lead of leads) {
|
|
109308
109308
|
const sys = lead.system_fields ?? {};
|
|
109309
109309
|
const custom = lead.custom_fields ?? {};
|
|
109310
|
-
const
|
|
109310
|
+
const name2 = custom["name"] ?? custom["full_name"] ?? "\u2014";
|
|
109311
109311
|
const email = custom["email"] ?? "\u2014";
|
|
109312
109312
|
const phone = custom["phone number"] ?? custom["phone"] ?? "\u2014";
|
|
109313
|
-
console.log(` [${sys.region ?? "\u2014"}] ${
|
|
109313
|
+
console.log(` [${sys.region ?? "\u2014"}] ${name2} ${email} ${phone}`);
|
|
109314
109314
|
console.log(
|
|
109315
109315
|
` \u8868\u5355: ${sys.form_name ?? "\u2014"} \u5E7F\u544A: ${sys.ad_name ?? "\u2014"} \u65F6\u95F4: ${sys.created_time ?? "\u2014"}`
|
|
109316
109316
|
);
|
|
@@ -109371,11 +109371,11 @@ Meta \u7EBF\u7D22\u8868\u5355\uFF08\u8D26\u6237 ${opts.account}\uFF0C\u7B2C 1 \u
|
|
|
109371
109371
|
if (f.name) acc[f.name] = (f.values ?? []).join(", ");
|
|
109372
109372
|
return acc;
|
|
109373
109373
|
}, {});
|
|
109374
|
-
const
|
|
109374
|
+
const name2 = fields["full_name"] ?? fields["name"] ?? "\u2014";
|
|
109375
109375
|
const email = fields["email"] ?? "\u2014";
|
|
109376
109376
|
const phone = fields["phone_number"] ?? fields["phone"] ?? "\u2014";
|
|
109377
109377
|
const formName = lead.form?.name ?? "\u2014";
|
|
109378
|
-
console.log(` ${
|
|
109378
|
+
console.log(` ${name2} ${email} ${phone}`);
|
|
109379
109379
|
console.log(` \u8868\u5355: ${formName} \u65F6\u95F4: ${lead.created_Time ?? "\u2014"}`);
|
|
109380
109380
|
console.log();
|
|
109381
109381
|
}
|
|
@@ -110842,13 +110842,13 @@ function validateCampaignCreateConfigCore(cfg) {
|
|
|
110842
110842
|
"campaign.TargetContentNetwork \u5FC5\u987B\u4E3A false\uFF08\u641C\u7D22\u4E13\u5C5E\u65B9\u6848\uFF1A\u7981\u6B62\u5C55\u793A\u5E7F\u544A\u7F51\u7EDC\uFF09"
|
|
110843
110843
|
);
|
|
110844
110844
|
}
|
|
110845
|
-
const
|
|
110846
|
-
if (typeof
|
|
110845
|
+
const name2 = campaign["Name"];
|
|
110846
|
+
if (typeof name2 !== "string" || !name2.trim()) {
|
|
110847
110847
|
pushErr2(errors, "campaign.Name \u4E0D\u80FD\u4E3A\u7A7A");
|
|
110848
|
-
} else if (cfg.name?.trim() && cfg.name.trim() !==
|
|
110848
|
+
} else if (cfg.name?.trim() && cfg.name.trim() !== name2.trim()) {
|
|
110849
110849
|
pushWarn2(
|
|
110850
110850
|
warnings,
|
|
110851
|
-
`\u5916\u5C42 name\uFF08${cfg.name}\uFF09\u4E0E campaign.Name\uFF08${
|
|
110851
|
+
`\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
110852
|
);
|
|
110853
110853
|
}
|
|
110854
110854
|
const budget = campaign["Budget"];
|
|
@@ -111375,8 +111375,8 @@ async function runAdCampaignCreate(opts) {
|
|
|
111375
111375
|
let adCount = 0;
|
|
111376
111376
|
const groupNames = [];
|
|
111377
111377
|
for (const g of adGroups) {
|
|
111378
|
-
const
|
|
111379
|
-
if (typeof
|
|
111378
|
+
const name2 = g["Name"];
|
|
111379
|
+
if (typeof name2 === "string" && name2) groupNames.push(name2);
|
|
111380
111380
|
const kws = g["KeywordsForBatchJob"] ?? [];
|
|
111381
111381
|
for (const block of kws) kwCount += block.KeywordText?.length ?? 0;
|
|
111382
111382
|
const ads = g["AdsForBatchJob"] ?? [];
|
|
@@ -112592,9 +112592,9 @@ async function runAdGeoSearch(opts) {
|
|
|
112592
112592
|
}
|
|
112593
112593
|
for (const item of items) {
|
|
112594
112594
|
const id = String(item["id"] ?? "");
|
|
112595
|
-
const
|
|
112595
|
+
const name2 = String(item["locationName"] ?? item["canonicalName"] ?? item["name"] ?? "");
|
|
112596
112596
|
const type = String(item["targetType"] ?? item["typeV2"] ?? "");
|
|
112597
|
-
console.log(` id:${id} ${
|
|
112597
|
+
console.log(` id:${id} ${name2} [${type}]`);
|
|
112598
112598
|
}
|
|
112599
112599
|
console.log();
|
|
112600
112600
|
}
|
|
@@ -112650,14 +112650,14 @@ async function runAdGeoList(opts) {
|
|
|
112650
112650
|
}
|
|
112651
112651
|
for (const item of items) {
|
|
112652
112652
|
const id = String(item["id"] ?? item["countryCriteriaId"] ?? "");
|
|
112653
|
-
const
|
|
112653
|
+
const name2 = String(
|
|
112654
112654
|
item["name"] ?? item["countryOrRegion"] ?? item["country"] ?? item["location"] ?? item["canonicalName"] ?? ""
|
|
112655
112655
|
);
|
|
112656
112656
|
const campaign = item["campaignName"] ? ` campaign:${item["campaignName"]}` : "";
|
|
112657
112657
|
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
112658
|
const spend = item["spend"] != null ? ` \u6D88\u8017:${item["spend"]}` : "";
|
|
112659
112659
|
const clicks = item["clicks"] != null ? ` \u70B9\u51FB:${item["clicks"]}` : "";
|
|
112660
|
-
console.log(` id:${id} ${
|
|
112660
|
+
console.log(` id:${id} ${name2}${campaign}${bid}${spend}${clicks}`);
|
|
112661
112661
|
}
|
|
112662
112662
|
console.log();
|
|
112663
112663
|
}
|
|
@@ -112772,12 +112772,12 @@ async function runAdGeoSetBid(opts) {
|
|
|
112772
112772
|
process.exit(1);
|
|
112773
112773
|
}
|
|
112774
112774
|
const row = rows.find((r) => String(r["id"] ?? "") === criterionId);
|
|
112775
|
-
const
|
|
112775
|
+
const name2 = String(
|
|
112776
112776
|
row?.["locationName"] ?? row?.["name"] ?? row?.["countryOrRegion"] ?? criterionId
|
|
112777
112777
|
);
|
|
112778
112778
|
console.log(
|
|
112779
112779
|
`
|
|
112780
|
-
\u2705 \u5DF2\u66F4\u65B0\u5730\u7406\u4F4D\u7F6E\u51FA\u4EF7\uFF08\u7CFB\u5217 ${opts.campaignId} / ${
|
|
112780
|
+
\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
112781
|
`
|
|
112782
112782
|
);
|
|
112783
112783
|
}
|
|
@@ -113744,8 +113744,11 @@ function nonEmptyStrings(list) {
|
|
|
113744
113744
|
}
|
|
113745
113745
|
function hasImageSource(cfg, kind) {
|
|
113746
113746
|
const paths = cfg.imagePaths;
|
|
113747
|
+
const assetIdKey = kind === "marketing" ? "marketingImageAssetId" : kind === "square" ? "squareMarketingImageAssetId" : "logoImageAssetId";
|
|
113747
113748
|
const base64Key = kind === "marketing" ? "marketingImageBase64" : kind === "square" ? "squareMarketingImageBase64" : "logoImageBase64";
|
|
113748
113749
|
const pathKey = kind;
|
|
113750
|
+
const assetId = cfg[assetIdKey];
|
|
113751
|
+
if (typeof assetId === "string" && assetId.trim().length > 0) return true;
|
|
113749
113752
|
const b64 = cfg[base64Key];
|
|
113750
113753
|
if (typeof b64 === "string" && b64.trim().length > 0) return true;
|
|
113751
113754
|
const p = paths?.[pathKey];
|
|
@@ -113822,13 +113825,22 @@ function runPmaxCreateValidation(cfg) {
|
|
|
113822
113825
|
pushErr3(errors, "businessName\uFF08\u5546\u5BB6\u540D\u79F0\uFF09\u5FC5\u586B");
|
|
113823
113826
|
}
|
|
113824
113827
|
if (!hasImageSource(cfg, "marketing")) {
|
|
113825
|
-
pushErr3(
|
|
113828
|
+
pushErr3(
|
|
113829
|
+
errors,
|
|
113830
|
+
"\u6A2A\u56FE\u5FC5\u586B\uFF1AimagePaths.marketing\uFF08\u63A8\u8350\uFF0C\u63D0\u4EA4\u65F6\u81EA\u52A8\u4E0A\u4F20\uFF09\u6216 marketingImageAssetId / marketingImageBase64"
|
|
113831
|
+
);
|
|
113826
113832
|
}
|
|
113827
113833
|
if (!hasImageSource(cfg, "square")) {
|
|
113828
|
-
pushErr3(
|
|
113834
|
+
pushErr3(
|
|
113835
|
+
errors,
|
|
113836
|
+
"\u65B9\u56FE\u5FC5\u586B\uFF1AimagePaths.square \u6216 squareMarketingImageAssetId / squareMarketingImageBase64"
|
|
113837
|
+
);
|
|
113829
113838
|
}
|
|
113830
113839
|
if (!hasImageSource(cfg, "logo")) {
|
|
113831
|
-
pushErr3(
|
|
113840
|
+
pushErr3(
|
|
113841
|
+
errors,
|
|
113842
|
+
"Logo \u5FC5\u586B\uFF1AimagePaths.logo \u6216 logoImageAssetId / logoImageBase64"
|
|
113843
|
+
);
|
|
113832
113844
|
}
|
|
113833
113845
|
const bidding = normalizeBidding(cfg.biddingStrategyTypeV2);
|
|
113834
113846
|
if (!bidding) {
|
|
@@ -114054,9 +114066,205 @@ async function runPmaxImageValidation(configFile, cfg) {
|
|
|
114054
114066
|
return { errors, warnings };
|
|
114055
114067
|
}
|
|
114056
114068
|
|
|
114057
|
-
// src/commands/ad/pmax-
|
|
114069
|
+
// src/commands/ad/pmax-image-resolve.ts
|
|
114070
|
+
import { basename as basename4, dirname as dirname8, isAbsolute as isAbsolute3, resolve as resolve7 } from "path";
|
|
114058
114071
|
import { readFileSync as readFileSync5 } from "fs";
|
|
114059
|
-
|
|
114072
|
+
|
|
114073
|
+
// src/commands/ad/pmax-urls.ts
|
|
114074
|
+
function pmaxChannelTypesUrl(googleApiUrl) {
|
|
114075
|
+
const base = googleApiUrl.replace(/\/$/, "");
|
|
114076
|
+
return `${base}/campaign/pmax/channel-types`;
|
|
114077
|
+
}
|
|
114078
|
+
function pmaxCampaignUrl(googleApiUrl, accountId, campaignId) {
|
|
114079
|
+
const base = googleApiUrl.replace(/\/$/, "");
|
|
114080
|
+
const path22 = `${base}/accounts/${accountId}/campaign/pmax`;
|
|
114081
|
+
return campaignId ? `${path22}/${campaignId}` : path22;
|
|
114082
|
+
}
|
|
114083
|
+
function pmaxAssetGroupUrl(googleApiUrl, accountId, assetGroupId, suffix) {
|
|
114084
|
+
const base = googleApiUrl.replace(/\/$/, "");
|
|
114085
|
+
const path22 = `${base}/accounts/${accountId}/campaign/pmax/asset-group/${assetGroupId}`;
|
|
114086
|
+
return suffix ? `${path22}/${suffix.replace(/^\//, "")}` : path22;
|
|
114087
|
+
}
|
|
114088
|
+
function pmaxCampaignAssetGroupUrl(googleApiUrl, accountId, campaignId) {
|
|
114089
|
+
const base = googleApiUrl.replace(/\/$/, "");
|
|
114090
|
+
return `${base}/accounts/${accountId}/campaign/pmax/${campaignId}/asset-group`;
|
|
114091
|
+
}
|
|
114092
|
+
function pmaxUserlistsUrl(googleApiUrl, accountId) {
|
|
114093
|
+
const base = googleApiUrl.replace(/\/$/, "");
|
|
114094
|
+
return `${base}/campaignmanagement/userlists/${accountId}`;
|
|
114095
|
+
}
|
|
114096
|
+
function pmaxAudiencesUrl(googleApiUrl, accountId, limit) {
|
|
114097
|
+
const base = googleApiUrl.replace(/\/$/, "");
|
|
114098
|
+
const url = `${base}/campaignmanagement/audiences/${accountId}`;
|
|
114099
|
+
if (limit != null && Number.isFinite(limit)) {
|
|
114100
|
+
return `${url}?limit=${Math.floor(limit)}`;
|
|
114101
|
+
}
|
|
114102
|
+
return url;
|
|
114103
|
+
}
|
|
114104
|
+
function pmaxImageAssetUrl(googleApiUrl, accountId, name2) {
|
|
114105
|
+
const base = googleApiUrl.replace(/\/$/, "");
|
|
114106
|
+
const url = `${base}/mediamanagement/imageasset/${accountId}`;
|
|
114107
|
+
if (name2?.trim()) {
|
|
114108
|
+
return `${url}?name=${encodeURIComponent(name2.trim())}`;
|
|
114109
|
+
}
|
|
114110
|
+
return url;
|
|
114111
|
+
}
|
|
114112
|
+
function pmaxAssetGroupReportUrl(googleApiUrl, accountId, query) {
|
|
114113
|
+
const base = googleApiUrl.replace(/\/$/, "");
|
|
114114
|
+
const qs = query.toString();
|
|
114115
|
+
return `${base}/reporting/media-account/${accountId}/pmax/asset-groups/reports${qs ? `?${qs}` : ""}`;
|
|
114116
|
+
}
|
|
114117
|
+
function pmaxGeoReportUrl(googleApiUrl, accountId, query) {
|
|
114118
|
+
const base = googleApiUrl.replace(/\/$/, "");
|
|
114119
|
+
const qs = query.toString();
|
|
114120
|
+
return `${base}/reporting/media-account/${accountId}/pmax/campaigns/reports/geo${qs ? `?${qs}` : ""}`;
|
|
114121
|
+
}
|
|
114122
|
+
|
|
114123
|
+
// src/commands/ad/pmax-image-resolve.ts
|
|
114124
|
+
function resolvePmaxImagePath(configFile, relOrAbs) {
|
|
114125
|
+
const trimmed = relOrAbs.trim();
|
|
114126
|
+
if (isAbsolute3(trimmed)) return trimmed;
|
|
114127
|
+
return resolve7(dirname8(configFile), trimmed);
|
|
114128
|
+
}
|
|
114129
|
+
var SLOT_META = {
|
|
114130
|
+
marketing: {
|
|
114131
|
+
assetIdKey: "marketingImageAssetId",
|
|
114132
|
+
base64Key: "marketingImageBase64",
|
|
114133
|
+
pathKey: "marketing",
|
|
114134
|
+
configAssetIdKey: "marketingImageAssetId",
|
|
114135
|
+
configBase64Key: "marketingImageBase64",
|
|
114136
|
+
label: "\u6A2A\u56FE (MARKETING_IMAGE)"
|
|
114137
|
+
},
|
|
114138
|
+
square: {
|
|
114139
|
+
assetIdKey: "squareMarketingImageAssetId",
|
|
114140
|
+
base64Key: "squareMarketingImageBase64",
|
|
114141
|
+
pathKey: "square",
|
|
114142
|
+
configAssetIdKey: "squareMarketingImageAssetId",
|
|
114143
|
+
configBase64Key: "squareMarketingImageBase64",
|
|
114144
|
+
label: "\u65B9\u56FE (SQUARE_MARKETING_IMAGE)"
|
|
114145
|
+
},
|
|
114146
|
+
logo: {
|
|
114147
|
+
assetIdKey: "logoImageAssetId",
|
|
114148
|
+
base64Key: "logoImageBase64",
|
|
114149
|
+
pathKey: "logo",
|
|
114150
|
+
configAssetIdKey: "logoImageAssetId",
|
|
114151
|
+
configBase64Key: "logoImageBase64",
|
|
114152
|
+
label: "Logo (LOGO)"
|
|
114153
|
+
}
|
|
114154
|
+
};
|
|
114155
|
+
function guessContentType(fileName) {
|
|
114156
|
+
const lower = fileName.toLowerCase();
|
|
114157
|
+
if (lower.endsWith(".png")) return "image/png";
|
|
114158
|
+
if (lower.endsWith(".jpg") || lower.endsWith(".jpeg")) return "image/jpeg";
|
|
114159
|
+
if (lower.endsWith(".gif")) return "image/gif";
|
|
114160
|
+
if (lower.endsWith(".webp")) return "image/webp";
|
|
114161
|
+
return "application/octet-stream";
|
|
114162
|
+
}
|
|
114163
|
+
function normalizePmaxAssetId(value) {
|
|
114164
|
+
if (value == null) return "";
|
|
114165
|
+
if (typeof value === "number" && Number.isFinite(value)) return String(Math.trunc(value));
|
|
114166
|
+
const s = String(value).trim();
|
|
114167
|
+
if (!s) return "";
|
|
114168
|
+
const m = s.match(/\/assets\/(\d+)\s*$/);
|
|
114169
|
+
return m ? m[1] : s;
|
|
114170
|
+
}
|
|
114171
|
+
async function uploadPmaxImageFile(config, googleApiUrl, accountId, absImagePath, name2, verbose) {
|
|
114172
|
+
const fileName = basename4(absImagePath);
|
|
114173
|
+
const assetName = (name2 ?? fileName).trim() || fileName;
|
|
114174
|
+
const fileBuffer = readFileSync5(absImagePath);
|
|
114175
|
+
const mimeType = guessContentType(fileName);
|
|
114176
|
+
const form = new FormData();
|
|
114177
|
+
form.append("file", new Blob([fileBuffer], { type: mimeType }), fileName);
|
|
114178
|
+
const url = pmaxImageAssetUrl(googleApiUrl, accountId, assetName);
|
|
114179
|
+
const headers = {
|
|
114180
|
+
"Accept-Language": "zh-CN",
|
|
114181
|
+
Datapermission: config.dataPermission ?? ""
|
|
114182
|
+
};
|
|
114183
|
+
if (config.apiKey) headers["x-api-key"] = config.apiKey;
|
|
114184
|
+
else headers.Authorization = `Bearer ${config.authToken}`;
|
|
114185
|
+
const res = await fetch(url, { method: "POST", headers, body: form });
|
|
114186
|
+
const text = await res.text();
|
|
114187
|
+
if (!res.ok) {
|
|
114188
|
+
const detail = verbose ? `\uFF1A${text.slice(0, 300)}` : "";
|
|
114189
|
+
throw new Error(`\u4E0A\u4F20\u56FE\u7247\u8D44\u4EA7 HTTP ${res.status}${detail}\uFF08${absImagePath}\uFF09`);
|
|
114190
|
+
}
|
|
114191
|
+
let data;
|
|
114192
|
+
try {
|
|
114193
|
+
data = JSON.parse(text);
|
|
114194
|
+
} catch {
|
|
114195
|
+
throw new Error(`\u4E0A\u4F20\u56FE\u7247\u8D44\u4EA7\u54CD\u5E94\u975E JSON\uFF08${absImagePath}\uFF09`);
|
|
114196
|
+
}
|
|
114197
|
+
const id = normalizePmaxAssetId(data["id"]);
|
|
114198
|
+
if (!id) {
|
|
114199
|
+
throw new Error(`\u4E0A\u4F20\u56FE\u7247\u8D44\u4EA7\u672A\u8FD4\u56DE id\uFF08${absImagePath}\uFF09`);
|
|
114200
|
+
}
|
|
114201
|
+
return id;
|
|
114202
|
+
}
|
|
114203
|
+
async function resolveOneSlot(configFile, cfg, kind, accountId, config, googleApiUrl, verbose) {
|
|
114204
|
+
const meta = SLOT_META[kind];
|
|
114205
|
+
const presetId = String(cfg[meta.configAssetIdKey] ?? "").trim();
|
|
114206
|
+
if (presetId) {
|
|
114207
|
+
return { [meta.assetIdKey]: normalizePmaxAssetId(presetId) };
|
|
114208
|
+
}
|
|
114209
|
+
const inlineB64 = String(cfg[meta.configBase64Key] ?? "").trim();
|
|
114210
|
+
const pathRel = cfg.imagePaths?.[meta.pathKey]?.trim();
|
|
114211
|
+
if (pathRel) {
|
|
114212
|
+
const abs = resolvePmaxImagePath(configFile, pathRel);
|
|
114213
|
+
const id = await uploadPmaxImageFile(
|
|
114214
|
+
config,
|
|
114215
|
+
googleApiUrl,
|
|
114216
|
+
accountId,
|
|
114217
|
+
abs,
|
|
114218
|
+
basename4(abs),
|
|
114219
|
+
verbose
|
|
114220
|
+
);
|
|
114221
|
+
return { [meta.assetIdKey]: id };
|
|
114222
|
+
}
|
|
114223
|
+
if (inlineB64) {
|
|
114224
|
+
return { [meta.base64Key]: inlineB64 };
|
|
114225
|
+
}
|
|
114226
|
+
throw new Error(
|
|
114227
|
+
`${meta.label} \u672A\u914D\u7F6E\uFF1A\u8BF7\u63D0\u4F9B imagePaths.${String(meta.pathKey)}\u3001${String(meta.configBase64Key)} \u6216 ${String(meta.configAssetIdKey)}`
|
|
114228
|
+
);
|
|
114229
|
+
}
|
|
114230
|
+
async function resolvePmaxImageSlots(configFile, cfg, accountId, config, googleApiUrl, opts) {
|
|
114231
|
+
const kinds = ["marketing", "square", "logo"];
|
|
114232
|
+
const willUpload = kinds.some((k) => {
|
|
114233
|
+
const meta = SLOT_META[k];
|
|
114234
|
+
if (String(cfg[meta.configAssetIdKey] ?? "").trim()) return false;
|
|
114235
|
+
if (String(cfg[meta.configBase64Key] ?? "").trim()) return false;
|
|
114236
|
+
return Boolean(cfg.imagePaths?.[meta.pathKey]?.trim());
|
|
114237
|
+
});
|
|
114238
|
+
if (willUpload && opts?.logUploads !== false) {
|
|
114239
|
+
console.log("\n\u{1F4E4} \u6B63\u5728\u4E0A\u4F20 PMax \u56FE\u7247\u7D20\u6750\uFF08imagePaths \u2192 assetId\uFF09\u2026");
|
|
114240
|
+
}
|
|
114241
|
+
const parts = await Promise.all(
|
|
114242
|
+
kinds.map(
|
|
114243
|
+
(kind) => resolveOneSlot(configFile, cfg, kind, accountId, config, googleApiUrl, opts?.verbose)
|
|
114244
|
+
)
|
|
114245
|
+
);
|
|
114246
|
+
const merged = {};
|
|
114247
|
+
for (const part of parts) Object.assign(merged, part);
|
|
114248
|
+
if (willUpload && opts?.logUploads !== false) {
|
|
114249
|
+
console.log(
|
|
114250
|
+
` \u6A2A\u56FE id\uFF1A${merged.marketingImageAssetId ?? "(base64)"} \u65B9\u56FE id\uFF1A${merged.squareMarketingImageAssetId ?? "(base64)"} Logo id\uFF1A${merged.logoImageAssetId ?? "(base64)"}`
|
|
114251
|
+
);
|
|
114252
|
+
}
|
|
114253
|
+
return merged;
|
|
114254
|
+
}
|
|
114255
|
+
function assertPmaxImageSlotsResolved(slots) {
|
|
114256
|
+
for (const kind of ["marketing", "square", "logo"]) {
|
|
114257
|
+
const meta = SLOT_META[kind];
|
|
114258
|
+
const id = slots[meta.assetIdKey];
|
|
114259
|
+
const b64 = slots[meta.base64Key];
|
|
114260
|
+
if (!id && !b64) {
|
|
114261
|
+
throw new Error(`${meta.label} \u89E3\u6790\u540E\u4E3A\u7A7A`);
|
|
114262
|
+
}
|
|
114263
|
+
}
|
|
114264
|
+
}
|
|
114265
|
+
|
|
114266
|
+
// src/commands/ad/pmax-load.ts
|
|
114267
|
+
import { readFileSync as readFileSync6 } from "fs";
|
|
114060
114268
|
function loadPmaxCreateConfig(configFile) {
|
|
114061
114269
|
const cfg = tryLoadPmaxCreateConfig(configFile);
|
|
114062
114270
|
if (!cfg) {
|
|
@@ -114070,49 +114278,20 @@ function loadPmaxCreateConfig(configFile) {
|
|
|
114070
114278
|
function tryLoadPmaxCreateConfig(configFile) {
|
|
114071
114279
|
let raw;
|
|
114072
114280
|
try {
|
|
114073
|
-
raw = JSON.parse(
|
|
114281
|
+
raw = JSON.parse(readFileSync6(configFile, "utf8"));
|
|
114074
114282
|
} catch {
|
|
114075
114283
|
return null;
|
|
114076
114284
|
}
|
|
114077
114285
|
return stripMetaKeys(raw);
|
|
114078
114286
|
}
|
|
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
114287
|
function parseLocationLanguageIds(items) {
|
|
114095
114288
|
if (!items?.length) return void 0;
|
|
114096
114289
|
return items.map((item) => ({
|
|
114097
114290
|
id: Number(item.id)
|
|
114098
114291
|
}));
|
|
114099
114292
|
}
|
|
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
|
-
}
|
|
114293
|
+
function buildPmaxCreateApiBody(cfg, imageSlots) {
|
|
114294
|
+
assertPmaxImageSlotsResolved(imageSlots);
|
|
114116
114295
|
const body = {
|
|
114117
114296
|
name: cfg.name.trim(),
|
|
114118
114297
|
budget: toCentAmount(Number(cfg.budget)),
|
|
@@ -114121,9 +114300,7 @@ function buildPmaxCreateApiBody(configFile, cfg) {
|
|
|
114121
114300
|
longHeadlines: cfg.longHeadlines.map((h) => h.trim()).filter(Boolean),
|
|
114122
114301
|
descriptions: cfg.descriptions.map((d) => d.trim()).filter(Boolean),
|
|
114123
114302
|
businessName: cfg.businessName.trim(),
|
|
114124
|
-
|
|
114125
|
-
squareMarketingImageBase64,
|
|
114126
|
-
logoImageBase64
|
|
114303
|
+
...imageSlots
|
|
114127
114304
|
};
|
|
114128
114305
|
if (cfg.budgetName?.trim()) body.budgetName = cfg.budgetName.trim();
|
|
114129
114306
|
if (cfg.assetGroupName?.trim()) body.assetGroupName = cfg.assetGroupName.trim();
|
|
@@ -114171,9 +114348,25 @@ async function runAdPmaxCreate(opts) {
|
|
|
114171
114348
|
const config = await ensureDataPermission(loadConfig(opts.token));
|
|
114172
114349
|
const googleApiUrl = requireGoogleApi(config);
|
|
114173
114350
|
const accountId = cfg.account.toString().trim();
|
|
114351
|
+
let imageSlots;
|
|
114352
|
+
try {
|
|
114353
|
+
imageSlots = await resolvePmaxImageSlots(
|
|
114354
|
+
opts.configFile,
|
|
114355
|
+
cfg,
|
|
114356
|
+
accountId,
|
|
114357
|
+
config,
|
|
114358
|
+
googleApiUrl,
|
|
114359
|
+
{ verbose: opts.verbose, logUploads: !opts.json }
|
|
114360
|
+
);
|
|
114361
|
+
} catch (err) {
|
|
114362
|
+
console.error(`
|
|
114363
|
+
\u274C \u56FE\u7247\u4E0A\u4F20/\u89E3\u6790\u5931\u8D25\uFF1A${err instanceof Error ? err.message : String(err)}
|
|
114364
|
+
`);
|
|
114365
|
+
process.exit(1);
|
|
114366
|
+
}
|
|
114174
114367
|
let body;
|
|
114175
114368
|
try {
|
|
114176
|
-
body = buildPmaxCreateApiBody(
|
|
114369
|
+
body = buildPmaxCreateApiBody(cfg, imageSlots);
|
|
114177
114370
|
} catch (err) {
|
|
114178
114371
|
console.error(`
|
|
114179
114372
|
\u274C \u6784\u5EFA\u8BF7\u6C42\u4F53\u5931\u8D25\uFF1A${err instanceof Error ? err.message : String(err)}
|
|
@@ -114272,18 +114465,18 @@ async function runAdPmaxValidate(opts) {
|
|
|
114272
114465
|
}
|
|
114273
114466
|
|
|
114274
114467
|
// src/commands/ad/pmax-mgmt.ts
|
|
114275
|
-
import {
|
|
114468
|
+
import { basename as basename5, isAbsolute as isAbsolute5 } from "path";
|
|
114276
114469
|
|
|
114277
114470
|
// src/commands/ad/pmax-shared.ts
|
|
114278
114471
|
init_auth();
|
|
114279
114472
|
init_cli_json_snapshot();
|
|
114280
|
-
import { readFileSync as
|
|
114473
|
+
import { readFileSync as readFileSync7 } from "fs";
|
|
114281
114474
|
import { dirname as dirname9, isAbsolute as isAbsolute4, resolve as resolve8 } from "path";
|
|
114282
114475
|
var PMAX_MONEY_KEYS = /* @__PURE__ */ new Set(["budget", "targetCpa_BidingAmount"]);
|
|
114283
114476
|
function loadPmaxJsonFile(configFile) {
|
|
114284
114477
|
let raw;
|
|
114285
114478
|
try {
|
|
114286
|
-
raw = JSON.parse(
|
|
114479
|
+
raw = JSON.parse(readFileSync7(configFile, "utf8"));
|
|
114287
114480
|
} catch (e) {
|
|
114288
114481
|
const msg = e instanceof Error ? e.message : String(e);
|
|
114289
114482
|
console.error(`
|
|
@@ -114293,21 +114486,11 @@ function loadPmaxJsonFile(configFile) {
|
|
|
114293
114486
|
}
|
|
114294
114487
|
return stripMetaKeys(raw);
|
|
114295
114488
|
}
|
|
114296
|
-
function
|
|
114489
|
+
function resolvePmaxImagePath2(configFile, relOrAbs) {
|
|
114297
114490
|
const trimmed = relOrAbs.trim();
|
|
114298
114491
|
if (isAbsolute4(trimmed)) return trimmed;
|
|
114299
114492
|
return resolve8(dirname9(configFile), trimmed);
|
|
114300
114493
|
}
|
|
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
114494
|
function convertPmaxMoneyInObject(body) {
|
|
114312
114495
|
const out = { ...body };
|
|
114313
114496
|
for (const key of PMAX_MONEY_KEYS) {
|
|
@@ -114318,22 +114501,8 @@ function convertPmaxMoneyInObject(body) {
|
|
|
114318
114501
|
}
|
|
114319
114502
|
return out;
|
|
114320
114503
|
}
|
|
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
|
-
}
|
|
114504
|
+
function buildPmaxAssetGroupApiBody(cfg, imageSlots) {
|
|
114505
|
+
assertPmaxImageSlotsResolved(imageSlots);
|
|
114337
114506
|
return {
|
|
114338
114507
|
name: cfg.name.trim(),
|
|
114339
114508
|
finalUrls: cfg.finalUrls.map((u) => u.trim()).filter(Boolean),
|
|
@@ -114341,26 +114510,34 @@ function buildPmaxAssetGroupApiBody(configFile, cfg) {
|
|
|
114341
114510
|
longHeadlines: cfg.longHeadlines.map((h) => h.trim()).filter(Boolean),
|
|
114342
114511
|
descriptions: cfg.descriptions.map((d) => d.trim()).filter(Boolean),
|
|
114343
114512
|
businessName: cfg.businessName.trim(),
|
|
114344
|
-
|
|
114345
|
-
squareMarketingImageBase64,
|
|
114346
|
-
logoImageBase64
|
|
114513
|
+
...imageSlots
|
|
114347
114514
|
};
|
|
114348
114515
|
}
|
|
114349
|
-
function
|
|
114516
|
+
async function processAssetsUpdateBodyWithUpload(configFile, body, accountId, config, googleApiUrl, verbose) {
|
|
114350
114517
|
const out = { ...body };
|
|
114351
114518
|
const links = out["assetsToLink"];
|
|
114352
114519
|
if (!Array.isArray(links)) return out;
|
|
114353
|
-
out["assetsToLink"] =
|
|
114354
|
-
|
|
114355
|
-
|
|
114356
|
-
|
|
114357
|
-
|
|
114358
|
-
|
|
114359
|
-
|
|
114360
|
-
|
|
114361
|
-
|
|
114362
|
-
|
|
114363
|
-
|
|
114520
|
+
out["assetsToLink"] = await Promise.all(
|
|
114521
|
+
links.map(async (item) => {
|
|
114522
|
+
if (!item || typeof item !== "object") return item;
|
|
114523
|
+
const row = { ...item };
|
|
114524
|
+
const imagePath = row["imagePath"];
|
|
114525
|
+
if (typeof imagePath === "string" && imagePath.trim()) {
|
|
114526
|
+
const abs = resolvePmaxImagePath2(configFile, imagePath);
|
|
114527
|
+
row["assetId"] = await uploadPmaxImageFile(
|
|
114528
|
+
config,
|
|
114529
|
+
googleApiUrl,
|
|
114530
|
+
accountId,
|
|
114531
|
+
abs,
|
|
114532
|
+
void 0,
|
|
114533
|
+
verbose
|
|
114534
|
+
);
|
|
114535
|
+
delete row["imagePath"];
|
|
114536
|
+
delete row["imageBase64"];
|
|
114537
|
+
}
|
|
114538
|
+
return row;
|
|
114539
|
+
})
|
|
114540
|
+
);
|
|
114364
114541
|
return out;
|
|
114365
114542
|
}
|
|
114366
114543
|
async function withGoogleApi(opts, fn) {
|
|
@@ -114391,56 +114568,6 @@ function requireAccountId(account, label = "-a, --account") {
|
|
|
114391
114568
|
return id;
|
|
114392
114569
|
}
|
|
114393
114570
|
|
|
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
114571
|
// src/commands/ad/pmax-mgmt.ts
|
|
114445
114572
|
function buildReportQuery(opts) {
|
|
114446
114573
|
const params = new URLSearchParams();
|
|
@@ -114598,27 +114725,49 @@ async function runAdPmaxAssetGroupCreate(opts) {
|
|
|
114598
114725
|
console.error("\n\u274C JSON \u987B\u5305\u542B campaignId\n");
|
|
114599
114726
|
process.exit(1);
|
|
114600
114727
|
}
|
|
114601
|
-
|
|
114602
|
-
|
|
114603
|
-
|
|
114604
|
-
|
|
114605
|
-
|
|
114606
|
-
|
|
114607
|
-
|
|
114608
|
-
|
|
114609
|
-
|
|
114610
|
-
|
|
114611
|
-
|
|
114612
|
-
|
|
114613
|
-
|
|
114614
|
-
|
|
114615
|
-
}
|
|
114616
|
-
|
|
114728
|
+
const assetFields = {
|
|
114729
|
+
name: String(cfg["name"] ?? ""),
|
|
114730
|
+
finalUrls: cfg["finalUrls"] ?? [],
|
|
114731
|
+
businessName: String(cfg["businessName"] ?? ""),
|
|
114732
|
+
headlines: cfg["headlines"] ?? [],
|
|
114733
|
+
longHeadlines: cfg["longHeadlines"] ?? [],
|
|
114734
|
+
descriptions: cfg["descriptions"] ?? [],
|
|
114735
|
+
imagePaths: cfg["imagePaths"],
|
|
114736
|
+
marketingImageAssetId: cfg["marketingImageAssetId"],
|
|
114737
|
+
squareMarketingImageAssetId: cfg["squareMarketingImageAssetId"],
|
|
114738
|
+
logoImageAssetId: cfg["logoImageAssetId"],
|
|
114739
|
+
marketingImageBase64: cfg["marketingImageBase64"],
|
|
114740
|
+
squareMarketingImageBase64: cfg["squareMarketingImageBase64"],
|
|
114741
|
+
logoImageBase64: cfg["logoImageBase64"]
|
|
114742
|
+
};
|
|
114743
|
+
await withGoogleApi(opts, async (config, googleApiUrl) => {
|
|
114744
|
+
let imageSlots;
|
|
114745
|
+
try {
|
|
114746
|
+
imageSlots = await resolvePmaxImageSlots(
|
|
114747
|
+
opts.configFile,
|
|
114748
|
+
assetFields,
|
|
114749
|
+
accountId,
|
|
114750
|
+
config,
|
|
114751
|
+
googleApiUrl,
|
|
114752
|
+
{ verbose: opts.verbose, logUploads: !opts.json }
|
|
114753
|
+
);
|
|
114754
|
+
} catch (err) {
|
|
114755
|
+
console.error(
|
|
114756
|
+
`
|
|
114757
|
+
\u274C \u56FE\u7247\u4E0A\u4F20/\u89E3\u6790\u5931\u8D25\uFF1A${err instanceof Error ? err.message : String(err)}
|
|
114758
|
+
`
|
|
114759
|
+
);
|
|
114760
|
+
process.exit(1);
|
|
114761
|
+
}
|
|
114762
|
+
let body;
|
|
114763
|
+
try {
|
|
114764
|
+
body = buildPmaxAssetGroupApiBody(assetFields, imageSlots);
|
|
114765
|
+
} catch (err) {
|
|
114766
|
+
console.error(`
|
|
114617
114767
|
\u274C \u6784\u5EFA\u8BF7\u6C42\u4F53\u5931\u8D25\uFF1A${err instanceof Error ? err.message : String(err)}
|
|
114618
114768
|
`);
|
|
114619
|
-
|
|
114620
|
-
|
|
114621
|
-
await withGoogleApi(opts, async (config, googleApiUrl) => {
|
|
114769
|
+
process.exit(1);
|
|
114770
|
+
}
|
|
114622
114771
|
const url = pmaxCampaignAssetGroupUrl(googleApiUrl, accountId, campaignId);
|
|
114623
114772
|
let data;
|
|
114624
114773
|
try {
|
|
@@ -114716,17 +114865,24 @@ async function runAdPmaxAssetsUpdate(opts) {
|
|
|
114716
114865
|
console.error("\n\u274C JSON \u987B\u5305\u542B campaignId\uFF08PUT assets body \u5FC5\u586B\uFF09\n");
|
|
114717
114866
|
process.exit(1);
|
|
114718
114867
|
}
|
|
114719
|
-
|
|
114720
|
-
|
|
114721
|
-
|
|
114722
|
-
|
|
114723
|
-
|
|
114724
|
-
|
|
114868
|
+
await withGoogleApi(opts, async (config, googleApiUrl) => {
|
|
114869
|
+
let body;
|
|
114870
|
+
try {
|
|
114871
|
+
const { account: _a, ...rest } = cfg;
|
|
114872
|
+
body = await processAssetsUpdateBodyWithUpload(
|
|
114873
|
+
opts.configFile,
|
|
114874
|
+
rest,
|
|
114875
|
+
accountId,
|
|
114876
|
+
config,
|
|
114877
|
+
googleApiUrl,
|
|
114878
|
+
opts.verbose
|
|
114879
|
+
);
|
|
114880
|
+
} catch (err) {
|
|
114881
|
+
console.error(`
|
|
114725
114882
|
\u274C \u5904\u7406 assets JSON \u5931\u8D25\uFF1A${err instanceof Error ? err.message : String(err)}
|
|
114726
114883
|
`);
|
|
114727
|
-
|
|
114728
|
-
|
|
114729
|
-
await withGoogleApi(opts, async (config, googleApiUrl) => {
|
|
114884
|
+
process.exit(1);
|
|
114885
|
+
}
|
|
114730
114886
|
const url = pmaxAssetGroupUrl(googleApiUrl, accountId, assetGroupId, "assets");
|
|
114731
114887
|
try {
|
|
114732
114888
|
await pmaxApiFetch(
|
|
@@ -114967,43 +115123,55 @@ PMax \u53D7\u4F17\u6570\u636E\u6E90\uFF08\u8D26\u6237 ${accountId}\uFF09
|
|
|
114967
115123
|
}
|
|
114968
115124
|
async function runAdPmaxImageUpload(opts) {
|
|
114969
115125
|
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
115126
|
await withGoogleApi(opts, async (config, googleApiUrl) => {
|
|
114994
|
-
const url = pmaxImageAssetUrl(googleApiUrl, accountId, name);
|
|
114995
115127
|
let data;
|
|
114996
115128
|
try {
|
|
114997
|
-
|
|
114998
|
-
|
|
114999
|
-
|
|
115000
|
-
|
|
115001
|
-
|
|
115002
|
-
|
|
115003
|
-
|
|
115004
|
-
|
|
115005
|
-
|
|
115006
|
-
|
|
115129
|
+
if (opts.imagePath?.trim()) {
|
|
115130
|
+
const abs = isAbsolute5(opts.imagePath) ? opts.imagePath.trim() : resolvePmaxImagePath2(process.cwd(), opts.imagePath);
|
|
115131
|
+
const name2 = (opts.name ?? basename5(abs)).trim();
|
|
115132
|
+
const id = await uploadPmaxImageFile(
|
|
115133
|
+
config,
|
|
115134
|
+
googleApiUrl,
|
|
115135
|
+
accountId,
|
|
115136
|
+
abs,
|
|
115137
|
+
name2,
|
|
115138
|
+
opts.verbose
|
|
115139
|
+
);
|
|
115140
|
+
data = { id, name: name2 };
|
|
115141
|
+
} else if (opts.bodyFile) {
|
|
115142
|
+
const cfg = loadPmaxJsonFile(opts.bodyFile);
|
|
115143
|
+
const name2 = String(cfg["name"] ?? opts.name ?? "cli-image").trim();
|
|
115144
|
+
const imagePath = String(cfg["imagePath"] ?? "").trim();
|
|
115145
|
+
if (imagePath) {
|
|
115146
|
+
const abs = resolvePmaxImagePath2(opts.bodyFile, imagePath);
|
|
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 {
|
|
115157
|
+
const base64String = String(cfg["base64String"] ?? cfg["Base64String"] ?? "").trim() || "";
|
|
115158
|
+
if (!base64String) {
|
|
115159
|
+
console.error("\n\u274C body JSON \u987B\u542B imagePath \u6216 base64String\n");
|
|
115160
|
+
process.exit(1);
|
|
115161
|
+
}
|
|
115162
|
+
const url = pmaxImageAssetUrl(googleApiUrl, accountId, name2);
|
|
115163
|
+
const raw = await pmaxApiFetch(
|
|
115164
|
+
url,
|
|
115165
|
+
config,
|
|
115166
|
+
{ method: "POST", body: JSON.stringify({ name: name2, base64String }) },
|
|
115167
|
+
opts.verbose
|
|
115168
|
+
);
|
|
115169
|
+
data = raw && typeof raw === "object" && !Array.isArray(raw) ? raw : { data: raw };
|
|
115170
|
+
}
|
|
115171
|
+
} else {
|
|
115172
|
+
console.error("\n\u274C \u8BF7\u6307\u5B9A --image-path \u6216 --body-file\n");
|
|
115173
|
+
process.exit(1);
|
|
115174
|
+
}
|
|
115007
115175
|
} catch (err) {
|
|
115008
115176
|
console.error(
|
|
115009
115177
|
`
|
|
@@ -115150,7 +115318,7 @@ async function runAdCampaignValidate(opts) {
|
|
|
115150
115318
|
|
|
115151
115319
|
// src/commands/ad/pmax-image-convert.ts
|
|
115152
115320
|
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
|
|
115321
|
+
import { resolve as resolve9, dirname as dirname10, basename as basename6, extname } from "path";
|
|
115154
115322
|
var SPECS = [
|
|
115155
115323
|
{ kind: "marketing", width: 1200, height: 628, fit: "cover", suffix: "_marketing" },
|
|
115156
115324
|
{ kind: "square", width: 1200, height: 1200, fit: "cover", suffix: "_square" },
|
|
@@ -115199,7 +115367,7 @@ async function runAdPmaxImageConvert(opts) {
|
|
|
115199
115367
|
const firstInput = input ?? inputMarketing ?? inputSquare ?? inputLogo;
|
|
115200
115368
|
const outputDir = opts.outputDir ? resolve9(opts.outputDir) : dirname10(resolve9(firstInput));
|
|
115201
115369
|
mkdirSync3(outputDir, { recursive: true });
|
|
115202
|
-
const prefix = opts.prefix ??
|
|
115370
|
+
const prefix = opts.prefix ?? basename6(firstInput, extname(firstInput));
|
|
115203
115371
|
const quality = opts.quality ?? 85;
|
|
115204
115372
|
const sharpMod = await loadSharp();
|
|
115205
115373
|
const sharpFn = (p) => sharpMod(p);
|
|
@@ -115662,7 +115830,7 @@ function register20(program2) {
|
|
|
115662
115830
|
}
|
|
115663
115831
|
);
|
|
115664
115832
|
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'
|
|
115833
|
+
'\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
115834
|
).requiredOption(
|
|
115667
115835
|
"--config-file <path>",
|
|
115668
115836
|
"JSON \u914D\u7F6E\u6587\u4EF6\uFF08\u6A21\u677F\u89C1 assets/siluzan-ads/assets/pmax-create-*.json|md\uFF09"
|
|
@@ -116780,9 +116948,9 @@ function splitQueryWhitespaceToKeywords(query) {
|
|
|
116780
116948
|
function ragItemDedupeKey(item) {
|
|
116781
116949
|
const id = String(item.id ?? "").trim();
|
|
116782
116950
|
if (id) return `id:${id}`;
|
|
116783
|
-
const
|
|
116951
|
+
const name2 = String(item.fields?.name ?? "").trim();
|
|
116784
116952
|
const content = String(item.fields?.content ?? "").slice(0, 160);
|
|
116785
|
-
return `fb:${
|
|
116953
|
+
return `fb:${name2}\0${content}`;
|
|
116786
116954
|
}
|
|
116787
116955
|
function mergeRagResultsByBestScore(batches) {
|
|
116788
116956
|
const best = /* @__PURE__ */ new Map();
|
|
@@ -116870,12 +117038,12 @@ function formatRagResultsMarkdown(query, items, subQueries) {
|
|
|
116870
117038
|
items.forEach((item, itemIndex) => {
|
|
116871
117039
|
const fields = item.fields;
|
|
116872
117040
|
if (!fields) return;
|
|
116873
|
-
const
|
|
117041
|
+
const name2 = fields.name ?? "\u672A\u77E5\u6587\u4EF6";
|
|
116874
117042
|
const content = fields.content ?? "";
|
|
116875
117043
|
const sourceUrl = fields.source_url ?? "";
|
|
116876
117044
|
const createTime = fields.create_time ?? "";
|
|
116877
117045
|
const usageCount = fields.usage_count ?? 0;
|
|
116878
|
-
parts.push(`### ${itemIndex + 1}. ${
|
|
117046
|
+
parts.push(`### ${itemIndex + 1}. ${name2}
|
|
116879
117047
|
`);
|
|
116880
117048
|
if (item.id) {
|
|
116881
117049
|
parts.push(`**\u7247\u6BB5 ID\uFF1A** \`${item.id}\`
|
|
@@ -118926,7 +119094,7 @@ init_cli_table();
|
|
|
118926
119094
|
async function uploadAttachment(filePath, apiBaseUrl, config, verbose) {
|
|
118927
119095
|
const fileName = path14.basename(filePath);
|
|
118928
119096
|
const fileBuffer = fs9.readFileSync(filePath);
|
|
118929
|
-
const mimeType =
|
|
119097
|
+
const mimeType = guessContentType2(fileName);
|
|
118930
119098
|
const form = new FormData();
|
|
118931
119099
|
form.append("file", new Blob([fileBuffer], { type: mimeType }), fileName);
|
|
118932
119100
|
const uploadUrl = `${apiBaseUrl}/command/attachment`;
|
|
@@ -118952,7 +119120,7 @@ async function uploadAttachment(filePath, apiBaseUrl, config, verbose) {
|
|
|
118952
119120
|
}
|
|
118953
119121
|
return { id: data.id, fileName, storageProvider: data.storageProvider ?? "StorageAccount" };
|
|
118954
119122
|
}
|
|
118955
|
-
function
|
|
119123
|
+
function guessContentType2(fileName) {
|
|
118956
119124
|
const ext = path14.extname(fileName).toLowerCase();
|
|
118957
119125
|
const map = {
|
|
118958
119126
|
".jpg": "image/jpeg",
|
|
@@ -119227,10 +119395,10 @@ async function runOpenAccountGoogleTimezones(opts) {
|
|
|
119227
119395
|
if (kw) {
|
|
119228
119396
|
rows = rows.filter((r) => {
|
|
119229
119397
|
const code = String(r.Code ?? "").toLowerCase();
|
|
119230
|
-
const
|
|
119398
|
+
const name2 = String(r.Name ?? "").toLowerCase();
|
|
119231
119399
|
const time = String(r.Time ?? "").toLowerCase();
|
|
119232
119400
|
const label = `(${r.Time ?? ""})${r.Name ?? ""}`.toLowerCase();
|
|
119233
|
-
return code.includes(kw) ||
|
|
119401
|
+
return code.includes(kw) || name2.includes(kw) || time.includes(kw) || label.includes(kw);
|
|
119234
119402
|
});
|
|
119235
119403
|
}
|
|
119236
119404
|
const n = rows.length;
|
|
@@ -119652,9 +119820,9 @@ async function runOpenAccountTikTokTimezones(opts) {
|
|
|
119652
119820
|
if (kw) {
|
|
119653
119821
|
rows = rows.filter((r) => {
|
|
119654
119822
|
const code = String(r.Code ?? "").toLowerCase();
|
|
119655
|
-
const
|
|
119823
|
+
const name2 = String(r.Name ?? "").toLowerCase();
|
|
119656
119824
|
const time = String(r.Time ?? "").toLowerCase();
|
|
119657
|
-
return code.includes(kw) ||
|
|
119825
|
+
return code.includes(kw) || name2.includes(kw) || time.includes(kw);
|
|
119658
119826
|
});
|
|
119659
119827
|
}
|
|
119660
119828
|
const n = rows.length;
|
|
@@ -119741,12 +119909,12 @@ TikTok \u884C\u4E1A\u5217\u8868\uFF08\u7B2C 1 \u9875\uFF0C\u672C\u9875 ${rows.le
|
|
|
119741
119909
|
}
|
|
119742
119910
|
for (const r of rows) {
|
|
119743
119911
|
const id = String(r.IndustryId ?? "");
|
|
119744
|
-
const
|
|
119912
|
+
const name2 = String(r.IndustryName ?? "");
|
|
119745
119913
|
const isTop = id.length >= 6 && id.slice(4, 6) === "01";
|
|
119746
119914
|
if (isTop) {
|
|
119747
|
-
console.log(` \u25B8 ${
|
|
119915
|
+
console.log(` \u25B8 ${name2} (${id})`);
|
|
119748
119916
|
} else {
|
|
119749
|
-
console.log(` ${
|
|
119917
|
+
console.log(` ${name2} (${id})`);
|
|
119750
119918
|
}
|
|
119751
119919
|
}
|
|
119752
119920
|
console.log();
|
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` |
|
|
@@ -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.3'
|
|
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.3"
|
|
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"
|