siluzan-tso-cli 1.1.47-beta.2 → 1.1.47-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 +64 -34
- package/dist/skill/_meta.json +2 -2
- package/dist/skill/references/analytics/account-analytics.md +1 -1
- package/dist/skill/references/google-ads/google-ads-read.md +1 -1
- package/dist/skill/references/google-ads/google-ads-write.md +11 -1
- package/dist/skill/references/report-templates/okki-weekly-google-client.md +1 -1
- package/dist/skill/report-templates/okki-weekly-google-client.md +1 -1
- 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.47-beta.
|
|
54
|
+
> **注意**:当前为测试版(1.1.47-beta.4),供内部测试使用。正式发布后安装命令将改为 `npm install -g siluzan-tso-cli`。
|
|
55
55
|
|
|
56
56
|
| 助手 | 建议 `--ai` |
|
|
57
57
|
| ----------------------- | ------------------------------------ |
|
package/dist/index.js
CHANGED
|
@@ -5964,6 +5964,23 @@ var init_strip_legacy_google_fields = __esm({
|
|
|
5964
5964
|
}
|
|
5965
5965
|
});
|
|
5966
5966
|
|
|
5967
|
+
// src/utils/bid-modifier-display.ts
|
|
5968
|
+
function formatBidModifierPercent(multiplier, specified) {
|
|
5969
|
+
if (multiplier === 0 && specified !== true) {
|
|
5970
|
+
return GOOGLE_ADS_UNSET_BID_DISPLAY;
|
|
5971
|
+
}
|
|
5972
|
+
const pct2 = Math.round((multiplier - 1) * 1e3) / 10;
|
|
5973
|
+
if (pct2 === 0) return "0%";
|
|
5974
|
+
return pct2 > 0 ? `+${pct2}%` : `${pct2}%`;
|
|
5975
|
+
}
|
|
5976
|
+
var GOOGLE_ADS_UNSET_BID_DISPLAY;
|
|
5977
|
+
var init_bid_modifier_display = __esm({
|
|
5978
|
+
"src/utils/bid-modifier-display.ts"() {
|
|
5979
|
+
"use strict";
|
|
5980
|
+
GOOGLE_ADS_UNSET_BID_DISPLAY = "\u2014";
|
|
5981
|
+
}
|
|
5982
|
+
});
|
|
5983
|
+
|
|
5967
5984
|
// src/utils/google-analysis-snapshot.ts
|
|
5968
5985
|
var init_google_analysis_snapshot = __esm({
|
|
5969
5986
|
"src/utils/google-analysis-snapshot.ts"() {
|
|
@@ -102027,18 +102044,22 @@ function deviceBidModifierLookupKey(campaignId, adGroupId, deviceType) {
|
|
|
102027
102044
|
function mergeDeviceBidModifiersIntoReport(report, modifiers) {
|
|
102028
102045
|
const rows = report.devices;
|
|
102029
102046
|
if (!Array.isArray(rows) || rows.length === 0) return report;
|
|
102030
|
-
if (!Array.isArray(modifiers) || modifiers.length === 0) return report;
|
|
102031
102047
|
const byKey = /* @__PURE__ */ new Map();
|
|
102032
|
-
|
|
102033
|
-
|
|
102048
|
+
if (Array.isArray(modifiers)) {
|
|
102049
|
+
for (const m of modifiers) {
|
|
102050
|
+
byKey.set(deviceBidModifierLookupKey(m.campaignId, m.adGroupId, m.deviceType), m);
|
|
102051
|
+
}
|
|
102034
102052
|
}
|
|
102035
102053
|
const devices = rows.map((row) => {
|
|
102036
102054
|
const hit = byKey.get(deviceBidModifierLookupKey(row.campaignId, row.adGroupId, row.deviceType)) ?? (row.adGroupId ? void 0 : byKey.get(deviceBidModifierLookupKey(row.campaignId, null, row.deviceType)));
|
|
102037
|
-
if (!hit || hit.bidModifier === void 0)
|
|
102055
|
+
if (!hit || hit.bidModifier === void 0) {
|
|
102056
|
+
return { ...row, bidModifierDisplay: GOOGLE_ADS_UNSET_BID_DISPLAY };
|
|
102057
|
+
}
|
|
102038
102058
|
return {
|
|
102039
102059
|
...row,
|
|
102040
102060
|
bidModifier: hit.bidModifier,
|
|
102041
|
-
bidModifierSpecified: hit.bidModifierSpecified
|
|
102061
|
+
bidModifierSpecified: hit.bidModifierSpecified,
|
|
102062
|
+
bidModifierDisplay: formatBidModifierPercent(hit.bidModifier, hit.bidModifierSpecified)
|
|
102042
102063
|
};
|
|
102043
102064
|
});
|
|
102044
102065
|
return { ...report, devices };
|
|
@@ -102285,6 +102306,7 @@ var init_fetch = __esm({
|
|
|
102285
102306
|
"src/commands/google-analysis/fetch.ts"() {
|
|
102286
102307
|
"use strict";
|
|
102287
102308
|
init_auth();
|
|
102309
|
+
init_bid_modifier_display();
|
|
102288
102310
|
init_strip_legacy_google_fields();
|
|
102289
102311
|
init_http_retry();
|
|
102290
102312
|
init_sections();
|
|
@@ -105841,7 +105863,15 @@ function register5(program2) {
|
|
|
105841
105863
|
// src/commands/list-accounts/_shared.ts
|
|
105842
105864
|
init_dist();
|
|
105843
105865
|
init_auth();
|
|
105844
|
-
|
|
105866
|
+
var SCOPE_ACTIVATION_BYPASSED_HEADER = "x-scope-activation-bypassed";
|
|
105867
|
+
function isScopeActivationBypassed(headers) {
|
|
105868
|
+
if (!headers) return false;
|
|
105869
|
+
const raw = headers[SCOPE_ACTIVATION_BYPASSED_HEADER] ?? headers["X-Scope-Activation-Bypassed"];
|
|
105870
|
+
const v = raw?.trim().toLowerCase();
|
|
105871
|
+
return v === "1" || v === "true" || v === "yes";
|
|
105872
|
+
}
|
|
105873
|
+
function shouldExposeScopeActivatedSources(bypassed = false) {
|
|
105874
|
+
if (bypassed) return false;
|
|
105845
105875
|
return resolveCompanySourceType() === "Ctaiad";
|
|
105846
105876
|
}
|
|
105847
105877
|
var VALID_MEDIA_TYPES = [
|
|
@@ -105966,7 +105996,7 @@ async function fetchTikTokAccountByMediaCustomerId(config, mediaCustomerId, verb
|
|
|
105966
105996
|
const match = items2.find((it) => String(it.ma?.mediaCustomerId ?? "") === id);
|
|
105967
105997
|
return match ?? null;
|
|
105968
105998
|
}
|
|
105969
|
-
function stripListAccountsEnrichmentFields(item) {
|
|
105999
|
+
function stripListAccountsEnrichmentFields(item, exposeScopeActivatedSources = shouldExposeScopeActivatedSources()) {
|
|
105970
106000
|
delete item.ma.remainingAccountBudget;
|
|
105971
106001
|
delete item.ma.platformStatus;
|
|
105972
106002
|
delete item.ma.aritScore;
|
|
@@ -105982,7 +106012,7 @@ function stripListAccountsEnrichmentFields(item) {
|
|
|
105982
106012
|
]) {
|
|
105983
106013
|
delete maExt[key];
|
|
105984
106014
|
}
|
|
105985
|
-
if (!
|
|
106015
|
+
if (!exposeScopeActivatedSources) {
|
|
105986
106016
|
delete item.ma.scopeActivatedSources;
|
|
105987
106017
|
delete maExt["scopeActivatedSources"];
|
|
105988
106018
|
}
|
|
@@ -106087,8 +106117,8 @@ var COL_GOOGLE_BASE = [
|
|
|
106087
106117
|
{ key: "shared", header: "\u5206\u4EAB" }
|
|
106088
106118
|
];
|
|
106089
106119
|
var COL_GOOGLE_PACKAGE = { key: "packageStatus", header: "\u5957\u9910\u6FC0\u6D3B" };
|
|
106090
|
-
function googleTableColumns() {
|
|
106091
|
-
if (!
|
|
106120
|
+
function googleTableColumns(exposePackage = shouldExposeScopeActivatedSources()) {
|
|
106121
|
+
if (!exposePackage) return COL_GOOGLE_BASE;
|
|
106092
106122
|
const authIdx = COL_GOOGLE_BASE.findIndex((c) => c.key === "authStatus");
|
|
106093
106123
|
const cols = [...COL_GOOGLE_BASE];
|
|
106094
106124
|
cols.splice(authIdx + 1, 0, COL_GOOGLE_PACKAGE);
|
|
@@ -106262,8 +106292,7 @@ function printTikTokTable(items2, tableOpts) {
|
|
|
106262
106292
|
});
|
|
106263
106293
|
printCliTable(rows, COL_TIKTOK, tableOpts);
|
|
106264
106294
|
}
|
|
106265
|
-
function printGoogleTable(items2, tableOpts) {
|
|
106266
|
-
const exposePackage = shouldExposeScopeActivatedSources();
|
|
106295
|
+
function printGoogleTable(items2, tableOpts, exposePackage = shouldExposeScopeActivatedSources()) {
|
|
106267
106296
|
const rows = items2.map((item) => {
|
|
106268
106297
|
const ma = item.ma;
|
|
106269
106298
|
const platformStatus = ma.platformStatus ?? "-";
|
|
@@ -106289,7 +106318,7 @@ function printGoogleTable(items2, tableOpts) {
|
|
|
106289
106318
|
}
|
|
106290
106319
|
return row;
|
|
106291
106320
|
});
|
|
106292
|
-
printCliTable(rows, googleTableColumns(), tableOpts);
|
|
106321
|
+
printCliTable(rows, googleTableColumns(exposePackage), tableOpts);
|
|
106293
106322
|
}
|
|
106294
106323
|
var COL_DEFAULT_WITH_MEDIA = [
|
|
106295
106324
|
{ key: "mediaType", header: "\u5A92\u4F53\u7C7B\u578B" },
|
|
@@ -106364,6 +106393,7 @@ async function runListAccounts(opts) {
|
|
|
106364
106393
|
const platformCfg = opts.media ? PLATFORM_CONFIG[opts.media] : null;
|
|
106365
106394
|
let items2;
|
|
106366
106395
|
let total;
|
|
106396
|
+
let scopeActivationBypassed = false;
|
|
106367
106397
|
if (platformCfg) {
|
|
106368
106398
|
const params = new URLSearchParams();
|
|
106369
106399
|
for (const [k, v] of Object.entries(platformCfg.fixedParams)) {
|
|
@@ -106380,6 +106410,7 @@ async function runListAccounts(opts) {
|
|
|
106380
106410
|
try {
|
|
106381
106411
|
const res = await apiFetchWithHeaders2(url, config, {}, opts.verbose);
|
|
106382
106412
|
items2 = res.data ?? [];
|
|
106413
|
+
scopeActivationBypassed = isScopeActivationBypassed(res.headers);
|
|
106383
106414
|
const hit = res.headers["s-total-hits"];
|
|
106384
106415
|
if (hit !== void 0) total = parseInt(hit, 10) || void 0;
|
|
106385
106416
|
} catch (err) {
|
|
@@ -106389,22 +106420,22 @@ async function runListAccounts(opts) {
|
|
|
106389
106420
|
process.exit(1);
|
|
106390
106421
|
}
|
|
106391
106422
|
} else {
|
|
106392
|
-
let res;
|
|
106393
106423
|
try {
|
|
106394
|
-
res = await
|
|
106424
|
+
const res = await apiFetchWithHeaders2(url, config, {}, opts.verbose);
|
|
106425
|
+
items2 = Array.isArray(res.data?.mas) ? res.data.mas : [];
|
|
106426
|
+
total = typeof res.data?.total === "number" ? res.data.total : void 0;
|
|
106427
|
+
scopeActivationBypassed = isScopeActivationBypassed(res.headers);
|
|
106428
|
+
if (opts.media === "TikTok") {
|
|
106429
|
+
mergeTikTokAdList(items2, res.data?.adList);
|
|
106430
|
+
} else if (opts.media === "MetaAd") {
|
|
106431
|
+
mergeMetaFacebookAdList(items2, res.data?.adList);
|
|
106432
|
+
}
|
|
106395
106433
|
} catch (err) {
|
|
106396
106434
|
console.error(`
|
|
106397
106435
|
\u274C \u67E5\u8BE2\u5931\u8D25\uFF1A${err instanceof Error ? err.message : String(err)}
|
|
106398
106436
|
`);
|
|
106399
106437
|
process.exit(1);
|
|
106400
106438
|
}
|
|
106401
|
-
items2 = Array.isArray(res?.mas) ? res.mas : [];
|
|
106402
|
-
total = typeof res?.total === "number" ? res.total : void 0;
|
|
106403
|
-
if (opts.media === "TikTok") {
|
|
106404
|
-
mergeTikTokAdList(items2, res.adList);
|
|
106405
|
-
} else if (opts.media === "MetaAd") {
|
|
106406
|
-
mergeMetaFacebookAdList(items2, res.adList);
|
|
106407
|
-
}
|
|
106408
106439
|
}
|
|
106409
106440
|
} else {
|
|
106410
106441
|
const params = new URLSearchParams();
|
|
@@ -106416,6 +106447,7 @@ async function runListAccounts(opts) {
|
|
|
106416
106447
|
try {
|
|
106417
106448
|
const res = await apiFetchWithHeaders2(url, config, {}, opts.verbose);
|
|
106418
106449
|
items2 = res.data ?? [];
|
|
106450
|
+
scopeActivationBypassed = isScopeActivationBypassed(res.headers);
|
|
106419
106451
|
const hit = res.headers["s-total-hits"];
|
|
106420
106452
|
if (hit !== void 0) total = parseInt(hit, 10) || void 0;
|
|
106421
106453
|
} catch (err) {
|
|
@@ -106425,9 +106457,10 @@ async function runListAccounts(opts) {
|
|
|
106425
106457
|
process.exit(1);
|
|
106426
106458
|
}
|
|
106427
106459
|
}
|
|
106460
|
+
const exposeScopeActivated = shouldExposeScopeActivatedSources(scopeActivationBypassed);
|
|
106428
106461
|
if (items2.length > 0) {
|
|
106429
106462
|
for (const item of items2) {
|
|
106430
|
-
stripListAccountsEnrichmentFields(item);
|
|
106463
|
+
stripListAccountsEnrichmentFields(item, exposeScopeActivated);
|
|
106431
106464
|
}
|
|
106432
106465
|
}
|
|
106433
106466
|
const oauthFailWarning = (() => {
|
|
@@ -106469,7 +106502,7 @@ async function runListAccounts(opts) {
|
|
|
106469
106502
|
const isKwai = opts.media === "Kwai";
|
|
106470
106503
|
const tableBorderOpts = opts.unicode ? { plain: false } : void 0;
|
|
106471
106504
|
if (isGoogle) {
|
|
106472
|
-
printGoogleTable(items2, tableBorderOpts);
|
|
106505
|
+
printGoogleTable(items2, tableBorderOpts, exposeScopeActivated);
|
|
106473
106506
|
} else if (isYandex) {
|
|
106474
106507
|
printYandexTable(items2, tableBorderOpts);
|
|
106475
106508
|
} else if (isTikTok) {
|
|
@@ -115304,11 +115337,8 @@ init_strip_legacy_google_fields();
|
|
|
115304
115337
|
// src/commands/ad/device-bid.ts
|
|
115305
115338
|
init_auth();
|
|
115306
115339
|
init_cli_json_snapshot();
|
|
115307
|
-
|
|
115308
|
-
|
|
115309
|
-
if (pct2 === 0) return "0%";
|
|
115310
|
-
return pct2 > 0 ? `+${pct2}%` : `${pct2}%`;
|
|
115311
|
-
}
|
|
115340
|
+
init_bid_modifier_display();
|
|
115341
|
+
init_bid_modifier_display();
|
|
115312
115342
|
function multiplierToAdGroupBidPercent(multiplier) {
|
|
115313
115343
|
return Math.round((multiplier - 1) * 100 * 100) / 100;
|
|
115314
115344
|
}
|
|
@@ -115407,7 +115437,7 @@ async function runAdDeviceBidList(opts) {
|
|
|
115407
115437
|
campaignName: row.campaignName,
|
|
115408
115438
|
deviceType: row.deviceType,
|
|
115409
115439
|
bidModifier: row.bidModifier,
|
|
115410
|
-
bidModifierDisplay: row.bidModifier != null ? formatBidModifierPercent(row.bidModifier) :
|
|
115440
|
+
bidModifierDisplay: row.bidModifier != null ? formatBidModifierPercent(row.bidModifier, row.bidModifierSpecified) : GOOGLE_ADS_UNSET_BID_DISPLAY,
|
|
115411
115441
|
bidModifierSpecified: row.bidModifierSpecified
|
|
115412
115442
|
}));
|
|
115413
115443
|
levelLabel = opts.campaignId ? `\u7CFB\u5217 ${opts.campaignId}` : "\u8D26\u6237\u4E0B\u5168\u90E8\u7CFB\u5217";
|
|
@@ -115505,7 +115535,7 @@ async function runAdDeviceBidSet(opts) {
|
|
|
115505
115535
|
}
|
|
115506
115536
|
console.log(
|
|
115507
115537
|
`
|
|
115508
|
-
\u2705 \u5DF2\u66F4\u65B0\u5E7F\u544A\u7EC4\u8BBE\u5907\u51FA\u4EF7\uFF08\u7CFB\u5217 ${opts.campaignId} / \u7EC4 ${opts.adGroupId} / id ${criterionId2} / ${deviceType ?? "?"} \u2192 ${formatBidModifierPercent(opts.bidModifier)}\uFF09
|
|
115538
|
+
\u2705 \u5DF2\u66F4\u65B0\u5E7F\u544A\u7EC4\u8BBE\u5907\u51FA\u4EF7\uFF08\u7CFB\u5217 ${opts.campaignId} / \u7EC4 ${opts.adGroupId} / id ${criterionId2} / ${deviceType ?? "?"} \u2192 ${formatBidModifierPercent(opts.bidModifier, true)}\uFF09
|
|
115509
115539
|
`
|
|
115510
115540
|
);
|
|
115511
115541
|
return;
|
|
@@ -115541,7 +115571,7 @@ async function runAdDeviceBidSet(opts) {
|
|
|
115541
115571
|
const deviceLabel = opts.deviceType ? normalizeDeviceType(opts.deviceType) : "\u8BBE\u5907";
|
|
115542
115572
|
console.log(
|
|
115543
115573
|
`
|
|
115544
|
-
\u2705 \u5DF2\u66F4\u65B0\u7CFB\u5217\u8BBE\u5907\u51FA\u4EF7\uFF08\u7CFB\u5217 ${opts.campaignId} / ${deviceLabel} / id ${criterionId} \u2192 \u500D\u7387 ${opts.bidModifier}\uFF0C${formatBidModifierPercent(opts.bidModifier)}\uFF09
|
|
115574
|
+
\u2705 \u5DF2\u66F4\u65B0\u7CFB\u5217\u8BBE\u5907\u51FA\u4EF7\uFF08\u7CFB\u5217 ${opts.campaignId} / ${deviceLabel} / id ${criterionId} \u2192 \u500D\u7387 ${opts.bidModifier}\uFF0C${formatBidModifierPercent(opts.bidModifier, true)}\uFF09
|
|
115545
115575
|
`
|
|
115546
115576
|
);
|
|
115547
115577
|
}
|
|
@@ -115856,7 +115886,7 @@ async function runAdGeoList(opts) {
|
|
|
115856
115886
|
item["name"] ?? item["countryOrRegion"] ?? item["country"] ?? item["location"] ?? item["canonicalName"] ?? ""
|
|
115857
115887
|
);
|
|
115858
115888
|
const campaign = item["campaignName"] ? ` campaign:${item["campaignName"]}` : "";
|
|
115859
|
-
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"]}` : "";
|
|
115889
|
+
const bid = item["bidModifier"] != null && typeof item["bidModifier"] === "number" ? ` \u51FA\u4EF7\u8C03\u6574:${formatBidModifierPercent(item["bidModifier"], item["bidModifierSpecified"])}` : item["bidModifier"] != null ? ` \u51FA\u4EF7\u8C03\u6574:${item["bidModifier"]}` : "";
|
|
115860
115890
|
const spend = item["spend"] != null ? ` \u6D88\u8017:${item["spend"]}` : "";
|
|
115861
115891
|
const clicks = item["clicks"] != null ? ` \u70B9\u51FB:${item["clicks"]}` : "";
|
|
115862
115892
|
console.log(` id:${id} ${name2}${campaign}${bid}${spend}${clicks}`);
|
|
@@ -115986,7 +116016,7 @@ async function runAdGeoSetBid(opts) {
|
|
|
115986
116016
|
);
|
|
115987
116017
|
console.log(
|
|
115988
116018
|
`
|
|
115989
|
-
\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
|
|
116019
|
+
\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, true)}\uFF09
|
|
115990
116020
|
`
|
|
115991
116021
|
);
|
|
115992
116022
|
}
|
package/dist/skill/_meta.json
CHANGED
|
@@ -130,7 +130,7 @@ siluzan-tso google-analysis -a <id> --exclude materials,gold-account --json-out
|
|
|
130
130
|
| `geo-matched` | 相符地理位置(Matched locations,`user_location_view`)账户级按国家/地区汇总 |
|
|
131
131
|
| `campaign-geo` | 广告系列维度地理(可选 `--cost-greater` / `--click-greater` / `--conversions-greater` 与网关一致) |
|
|
132
132
|
| `campaign-geo-matched` | 相符地理位置(Matched locations)系列级明细(可选同上阈值参数) |
|
|
133
|
-
| `campaign-device` |
|
|
133
|
+
| `campaign-device` | 广告系列维度设备(行可含系列/组)。出价调整展示用 `bidModifierDisplay`(对齐 Google 后台:未设置=`—`,`0.6`=`-40%`);`bidModifier` 仍是倍率,禁止自行换成正百分比 |
|
|
134
134
|
| `audience` | 受众;可选 `--audience-type` |
|
|
135
135
|
| `asset-images` | 图片素材 |
|
|
136
136
|
| `videos` | 视频 |
|
|
@@ -301,6 +301,6 @@ siluzan-tso ad geo remove -a <accountId> --campaign-id <id> --location-id <id>
|
|
|
301
301
|
|
|
302
302
|
- `add`:写入 `PUT …/criterion/{account}` 时 CLI 会换算为后端百分比。
|
|
303
303
|
- `set-bid`:直接设置出价系数;与 `ad device-bid set`(系列级)同口径。
|
|
304
|
-
- `list` 返回的 `bidModifier` 为 Google **倍率**(如 `1.2` = +20
|
|
304
|
+
- `list` 返回的 `bidModifier` 为 Google **倍率**(如 `1.2` = +20%),不是百分比整数。未设置的 `0` 展示为 `—`(与 Google 后台一致),不要写成 `-100%`。
|
|
305
305
|
|
|
306
306
|
---
|
|
@@ -443,7 +443,17 @@ siluzan-tso ad extension delete -a <accountId> --id <extensionId>
|
|
|
443
443
|
| `1.0` | 不调整 |
|
|
444
444
|
| `0.8` | 降低 20% |
|
|
445
445
|
| `1.2` | 提高 20% |
|
|
446
|
-
| `0` |
|
|
446
|
+
| `0` | **写入**时排除该设备(-100%)。**列表**里网关常把「未设置」也填成 `0`,此时 Google 后台显示 `—`,不是 -100% |
|
|
447
|
+
|
|
448
|
+
**展示口径(与 Google Ads 后台「出价调整」列一致)**:`list` / `google-analysis --sections campaign-device` 已输出 `bidModifierDisplay`。写表格必须用这个字段,禁止把倍率当正百分比(不要把 `0.6` 写成 `40%`)。
|
|
449
|
+
|
|
450
|
+
| `bidModifier` | `bidModifierSpecified` | `bidModifierDisplay` |
|
|
451
|
+
| ------------- | ---------------------- | -------------------- |
|
|
452
|
+
| `0`(未设置) | 空 / `false` | `—` |
|
|
453
|
+
| `0`(明确排除) | `true` | `-100%` |
|
|
454
|
+
| `1.0` | — | `0%` |
|
|
455
|
+
| `0.6` | — | `-40%` |
|
|
456
|
+
| `1.1` | — | `+10%` |
|
|
447
457
|
|
|
448
458
|
广告组级会在 CLI 内将倍率转为后端百分比:`(倍率 - 1) × 100`。
|
|
449
459
|
|
|
@@ -182,7 +182,7 @@ siluzan-tso google-analysis -a <mediaCustomerId> --start <S> --end <E> --json-ou
|
|
|
182
182
|
| R1 | `设备报告` |
|
|
183
183
|
| R2 | 统计区间 |
|
|
184
184
|
| R3(A→M,13 列) | `设备` \| `级别` \| `广告系列` \| `出价调整` \| `展示次数` \| `点击次数` \| `点击率` \| `平均每次点击费用` \| `费用` \| `所有转化次数` \| `转化次数` \| `每次转化费用` \| `转化率` |
|
|
185
|
-
| R4… | `campaign-device-*.json`(`
|
|
185
|
+
| R4… | `campaign-device-*.json`(`items[]`):`deviceType`→设备;`campaignName` / `adGroupName`→「广告系列」等;`allConversions`→「所有转化次数」;`conversions`→「转化次数」;`ctr` / `conversionRate` 直接写入;**出价调整**:写 `bidModifierDisplay`(与 Google 后台一致:未设置=`—`,`0.6`=`-40%`)。禁止把倍率当正百分比,禁止把未设置的 `0` 写成 `-100%`;**级别**列按运营约定(如填 `广告系列` 或 `账户`,全文一致) |
|
|
186
186
|
| 表末「分析」 | **总结**:各设备(Mobile/Desktop/Tablet 等)消耗与转化占比、系列×设备高消耗组合、移动端 vs 桌面 CPA/CPC 差异。**建议**:设备出价系数调整或系列预算倾斜(须引用设备类型与表中费用/转化),1~3 条 |
|
|
187
187
|
|
|
188
188
|
---
|
|
@@ -182,7 +182,7 @@ siluzan-tso google-analysis -a <mediaCustomerId> --start <S> --end <E> --json-ou
|
|
|
182
182
|
| R1 | `设备报告` |
|
|
183
183
|
| R2 | 统计区间 |
|
|
184
184
|
| R3(A→M,13 列) | `设备` \| `级别` \| `广告系列` \| `出价调整` \| `展示次数` \| `点击次数` \| `点击率` \| `平均每次点击费用` \| `费用` \| `所有转化次数` \| `转化次数` \| `每次转化费用` \| `转化率` |
|
|
185
|
-
| R4… | `campaign-device-*.json`(`
|
|
185
|
+
| R4… | `campaign-device-*.json`(`items[]`):`deviceType`→设备;`campaignName` / `adGroupName`→「广告系列」等;`allConversions`→「所有转化次数」;`conversions`→「转化次数」;`ctr` / `conversionRate` 直接写入;**出价调整**:写 `bidModifierDisplay`(与 Google 后台一致:未设置=`—`,`0.6`=`-40%`)。禁止把倍率当正百分比,禁止把未设置的 `0` 写成 `-100%`;**级别**列按运营约定(如填 `广告系列` 或 `账户`,全文一致) |
|
|
186
186
|
| 表末「分析」 | **总结**:各设备(Mobile/Desktop/Tablet 等)消耗与转化占比、系列×设备高消耗组合、移动端 vs 桌面 CPA/CPC 差异。**建议**:设备出价系数调整或系列预算倾斜(须引用设备类型与表中费用/转化),1~3 条 |
|
|
187
187
|
|
|
188
188
|
---
|
|
@@ -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.47-beta.
|
|
12
|
+
$PKG_VERSION = '1.1.47-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.47-beta.
|
|
12
|
+
readonly PKG_VERSION="1.1.47-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"
|