siluzan-tso-cli 1.1.20-beta.14 → 1.1.20-beta.15
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 +162 -37
- package/dist/skill/SKILL.md +2 -2
- package/dist/skill/_meta.json +2 -2
- package/dist/skill/references/account-analytics.md +1 -1
- package/dist/skill/references/currency.md +1 -1
- package/dist/skill/references/google-ads-rules/google-ads-keyword-taxonomy.md +1 -1
- package/dist/skill/references/keyword-planner-workflows.md +4 -5
- 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.20-beta.
|
|
54
|
+
> **注意**:当前为测试版(1.1.20-beta.15),供内部测试使用。正式发布后安装命令将改为 `npm install -g siluzan-tso-cli`。
|
|
55
55
|
|
|
56
56
|
| 助手 | 建议 `--ai` |
|
|
57
57
|
| ----------------------- | ------------------------------------ |
|
package/dist/index.js
CHANGED
|
@@ -2418,12 +2418,12 @@ function createVersionNotifier(opts) {
|
|
|
2418
2418
|
const HOURS_24 = 24 * 60 * 60 * 1e3;
|
|
2419
2419
|
const TTL_MAIN_TAG_MS = 60 * 60 * 1e3;
|
|
2420
2420
|
const TTL_MIN_REQUIRED_MS = HOURS_24;
|
|
2421
|
-
async function fetchVersionByTag(tag,
|
|
2421
|
+
async function fetchVersionByTag(tag, cacheKey2, fetchAtKey, cfg, maxAgeMs) {
|
|
2422
2422
|
const lastAt = cfg[fetchAtKey];
|
|
2423
|
-
if (typeof lastAt === "string" &&
|
|
2423
|
+
if (typeof lastAt === "string" && cacheKey2 in cfg) {
|
|
2424
2424
|
const lastMs = new Date(lastAt).getTime();
|
|
2425
2425
|
if (Date.now() - lastMs < maxAgeMs) {
|
|
2426
|
-
const v = cfg[
|
|
2426
|
+
const v = cfg[cacheKey2];
|
|
2427
2427
|
const sv = typeof v === "string" && v ? v : null;
|
|
2428
2428
|
return { version: sv, hitNetwork: false };
|
|
2429
2429
|
}
|
|
@@ -4118,7 +4118,7 @@ async function fetchBalanceMap(media, accountIds, config, startDate, endDate, ve
|
|
|
4118
4118
|
return result;
|
|
4119
4119
|
}
|
|
4120
4120
|
function parseGoogleAccountSpendOverviewRows(raw) {
|
|
4121
|
-
const
|
|
4121
|
+
const microsToYuan = (v) => {
|
|
4122
4122
|
if (v == null) return null;
|
|
4123
4123
|
const n = Number(v);
|
|
4124
4124
|
if (!Number.isFinite(n)) return null;
|
|
@@ -4138,8 +4138,8 @@ function parseGoogleAccountSpendOverviewRows(raw) {
|
|
|
4138
4138
|
currencyCode: it.currencyCode ?? void 0,
|
|
4139
4139
|
status: it.status ?? void 0,
|
|
4140
4140
|
remainingAccountBudget: typeof it.remainingAccountBudget === "number" ? it.remainingAccountBudget : void 0,
|
|
4141
|
-
adjustedSpendingLimitYuan:
|
|
4142
|
-
lastCostYuan:
|
|
4141
|
+
adjustedSpendingLimitYuan: microsToYuan(it.adjustedSpendingLimitMicros),
|
|
4142
|
+
lastCostYuan: microsToYuan(it.lastCostMicros)
|
|
4143
4143
|
};
|
|
4144
4144
|
};
|
|
4145
4145
|
if (Array.isArray(raw)) {
|
|
@@ -114107,6 +114107,59 @@ function register20(program2) {
|
|
|
114107
114107
|
|
|
114108
114108
|
// src/commands/keyword.ts
|
|
114109
114109
|
init_auth();
|
|
114110
|
+
|
|
114111
|
+
// src/utils/usd-cny-rate.ts
|
|
114112
|
+
init_auth();
|
|
114113
|
+
var RATE_CACHE_TTL_MS = 10 * 60 * 1e3;
|
|
114114
|
+
var rateCache = null;
|
|
114115
|
+
function cacheKey(config) {
|
|
114116
|
+
return `${config.apiBaseUrl.replace(/\/$/, "")}|USD|CNY`;
|
|
114117
|
+
}
|
|
114118
|
+
function parseExrate(data) {
|
|
114119
|
+
if (data == null || typeof data !== "object") return null;
|
|
114120
|
+
const raw = data.exrate;
|
|
114121
|
+
const n = typeof raw === "string" ? Number(raw) : Number(raw);
|
|
114122
|
+
if (!Number.isFinite(n) || n <= 0) return null;
|
|
114123
|
+
return n;
|
|
114124
|
+
}
|
|
114125
|
+
async function getUsdToCnyExchangeRate(config, verbose) {
|
|
114126
|
+
const key = cacheKey(config);
|
|
114127
|
+
const now = Date.now();
|
|
114128
|
+
if (rateCache && rateCache.key === key && now - rateCache.fetchedAt < RATE_CACHE_TTL_MS) {
|
|
114129
|
+
if (verbose) {
|
|
114130
|
+
console.error(` [\u6C47\u7387] \u4F7F\u7528\u7F13\u5B58 USD\u2192CNY=${rateCache.rate}\uFF08${Math.round((now - rateCache.fetchedAt) / 1e3)}s \u524D\uFF09`);
|
|
114131
|
+
}
|
|
114132
|
+
return rateCache.rate;
|
|
114133
|
+
}
|
|
114134
|
+
const base = config.apiBaseUrl.replace(/\/$/, "");
|
|
114135
|
+
const url = `${base}/allinpay/GetRate?srcccy=USD&dstccy=CNY`;
|
|
114136
|
+
try {
|
|
114137
|
+
const data = await apiFetch2(url, config, { method: "GET" }, verbose);
|
|
114138
|
+
const rate = parseExrate(data);
|
|
114139
|
+
if (rate == null) {
|
|
114140
|
+
console.error("\n\u26A0 \u83B7\u53D6\u6C47\u7387\u5931\u8D25\uFF1AGetRate \u672A\u8FD4\u56DE\u6709\u6548 exrate\uFF0C\u51FA\u4EF7 CNY \u5B57\u6BB5\u5C06\u7701\u7565\u3002\n");
|
|
114141
|
+
return null;
|
|
114142
|
+
}
|
|
114143
|
+
rateCache = { key, rate, fetchedAt: now };
|
|
114144
|
+
if (verbose) {
|
|
114145
|
+
console.error(` [\u6C47\u7387] USD\u2192CNY=${rate}\uFF08\u5DF2\u7F13\u5B58 ${RATE_CACHE_TTL_MS / 6e4} \u5206\u949F\uFF09`);
|
|
114146
|
+
}
|
|
114147
|
+
return rate;
|
|
114148
|
+
} catch (err) {
|
|
114149
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
114150
|
+
console.error(`
|
|
114151
|
+
\u26A0 \u83B7\u53D6 USD\u2192CNY \u6C47\u7387\u5931\u8D25\uFF08\u51FA\u4EF7 CNY \u5B57\u6BB5\u5C06\u7701\u7565\uFF09\uFF1A${msg}
|
|
114152
|
+
`);
|
|
114153
|
+
return null;
|
|
114154
|
+
}
|
|
114155
|
+
}
|
|
114156
|
+
function convertUsdToCny(amountUsd, rate) {
|
|
114157
|
+
if (amountUsd == null || !Number.isFinite(amountUsd)) return null;
|
|
114158
|
+
if (!Number.isFinite(rate) || rate <= 0) return null;
|
|
114159
|
+
return Math.round(amountUsd * rate * 100) / 100;
|
|
114160
|
+
}
|
|
114161
|
+
|
|
114162
|
+
// src/commands/keyword.ts
|
|
114110
114163
|
init_cli_json_snapshot();
|
|
114111
114164
|
init_cli_table();
|
|
114112
114165
|
|
|
@@ -114169,12 +114222,25 @@ function requireGoogleApi2(config) {
|
|
|
114169
114222
|
return config.googleApiUrl;
|
|
114170
114223
|
}
|
|
114171
114224
|
var KEYWORD_SUGGEST_BID_AMOUNT_CURRENCY = "USD";
|
|
114172
|
-
function
|
|
114173
|
-
|
|
114174
|
-
|
|
114225
|
+
function applyUsdBidAmountsInCny(item, usdToCnyRate) {
|
|
114226
|
+
return {
|
|
114227
|
+
...item,
|
|
114228
|
+
averageCpcCNY: convertUsdToCny(item.averageCpcUSD, usdToCnyRate),
|
|
114229
|
+
lowTopOfPageBidCNY: convertUsdToCny(item.lowTopOfPageBidUSD, usdToCnyRate),
|
|
114230
|
+
highTopOfPageBidCNY: convertUsdToCny(item.highTopOfPageBidUSD, usdToCnyRate)
|
|
114231
|
+
};
|
|
114232
|
+
}
|
|
114233
|
+
function buildKeywordSuggestJsonPayload(items, opts) {
|
|
114234
|
+
let usdItems = items.map(microsItemToUSD);
|
|
114235
|
+
const rate = opts?.usdToCnyRate;
|
|
114236
|
+
if (rate != null && Number.isFinite(rate) && rate > 0) {
|
|
114237
|
+
usdItems = usdItems.map((row) => applyUsdBidAmountsInCny(row, rate));
|
|
114238
|
+
}
|
|
114239
|
+
const n = usdItems.length;
|
|
114175
114240
|
return {
|
|
114176
|
-
...wrapListJson({ page: 1, pageSize: Math.max(n, 1), total: n, items:
|
|
114177
|
-
bidAmountCurrency: KEYWORD_SUGGEST_BID_AMOUNT_CURRENCY
|
|
114241
|
+
...wrapListJson({ page: 1, pageSize: Math.max(n, 1), total: n, items: usdItems }),
|
|
114242
|
+
bidAmountCurrency: KEYWORD_SUGGEST_BID_AMOUNT_CURRENCY,
|
|
114243
|
+
...rate != null && Number.isFinite(rate) && rate > 0 ? { usdToCnyExchangeRate: rate } : {}
|
|
114178
114244
|
};
|
|
114179
114245
|
}
|
|
114180
114246
|
async function fetchUrlKeywords(url, keywords, verbose) {
|
|
@@ -114329,8 +114395,9 @@ async function runKeywordSuggest(opts) {
|
|
|
114329
114395
|
});
|
|
114330
114396
|
}
|
|
114331
114397
|
const n = items.length;
|
|
114332
|
-
const
|
|
114333
|
-
const
|
|
114398
|
+
const usdToCnyRate = await getUsdToCnyExchangeRate(config, opts.verbose);
|
|
114399
|
+
const kwPayload = buildKeywordSuggestJsonPayload(items, { usdToCnyRate });
|
|
114400
|
+
const usdItems = kwPayload.items;
|
|
114334
114401
|
if (await emitCliJsonOrSnapshot(opts, {
|
|
114335
114402
|
section: "keyword-suggest",
|
|
114336
114403
|
commandLabel: "keyword suggest",
|
|
@@ -114345,49 +114412,107 @@ async function runKeywordSuggest(opts) {
|
|
|
114345
114412
|
console.log(" \u6682\u65E0\u63A8\u8350\u5173\u952E\u8BCD\u3002\n");
|
|
114346
114413
|
return;
|
|
114347
114414
|
}
|
|
114348
|
-
const
|
|
114415
|
+
const fmtAmt = (v) => v != null && Number.isFinite(v) ? v.toFixed(2) : "\u2014";
|
|
114416
|
+
const hasCny = usdToCnyRate != null && usdToCnyRate > 0;
|
|
114417
|
+
const cols = hasCny ? [
|
|
114349
114418
|
{ key: "keyword", header: "\u5173\u952E\u8BCD" },
|
|
114350
114419
|
{ key: "montlySearch", header: "\u6708\u5747\u641C\u7D22" },
|
|
114351
|
-
{ key: "
|
|
114352
|
-
{ key: "
|
|
114420
|
+
{ key: "cpcUsd", header: "\u5E73\u5747CPC(USD)" },
|
|
114421
|
+
{ key: "cpcCny", header: "\u5E73\u5747CPC(CNY)" },
|
|
114422
|
+
{ key: "bidRangeUsd", header: "\u9875\u9996\u51FA\u4EF7(USD)" },
|
|
114423
|
+
{ key: "bidRangeCny", header: "\u9875\u9996\u51FA\u4EF7(CNY)" },
|
|
114424
|
+
{ key: "competition", header: "\u7ADE\u4E89\u5EA6" }
|
|
114425
|
+
] : [
|
|
114426
|
+
{ key: "keyword", header: "\u5173\u952E\u8BCD" },
|
|
114427
|
+
{ key: "montlySearch", header: "\u6708\u5747\u641C\u7D22" },
|
|
114428
|
+
{ key: "cpcUsd", header: "\u5E73\u5747CPC(USD)" },
|
|
114429
|
+
{ key: "bidRangeUsd", header: "\u9875\u9996\u51FA\u4EF7(USD)" },
|
|
114353
114430
|
{ key: "competition", header: "\u7ADE\u4E89\u5EA6" }
|
|
114354
114431
|
];
|
|
114355
|
-
const
|
|
114356
|
-
|
|
114357
|
-
const
|
|
114358
|
-
const
|
|
114359
|
-
const
|
|
114360
|
-
const bidRange = low === "\u2014" && high === "\u2014" ? "\u2014" : `${low} ~ ${high}`;
|
|
114432
|
+
const rows = usdItems.map((item) => {
|
|
114433
|
+
const cpcUsd = fmtAmt(item.averageCpcUSD);
|
|
114434
|
+
const lowUsd = fmtAmt(item.lowTopOfPageBidUSD);
|
|
114435
|
+
const highUsd = fmtAmt(item.highTopOfPageBidUSD);
|
|
114436
|
+
const bidRangeUsd = lowUsd === "\u2014" && highUsd === "\u2014" ? "\u2014" : `${lowUsd} ~ ${highUsd}`;
|
|
114361
114437
|
const competitionDisplay = item.competitionV2 ?? (item.competition != null ? item.competition.toFixed(2) : "\u2014");
|
|
114362
|
-
|
|
114438
|
+
const row = {
|
|
114363
114439
|
keyword: item.keyword ?? "",
|
|
114364
114440
|
montlySearch: String(item.montlySearch ?? "\u2014"),
|
|
114365
|
-
|
|
114366
|
-
|
|
114441
|
+
cpcUsd,
|
|
114442
|
+
bidRangeUsd,
|
|
114367
114443
|
competition: competitionDisplay
|
|
114368
114444
|
};
|
|
114445
|
+
if (hasCny) {
|
|
114446
|
+
const cpcCny = fmtAmt(item.averageCpcCNY);
|
|
114447
|
+
const lowCny = fmtAmt(item.lowTopOfPageBidCNY);
|
|
114448
|
+
const highCny = fmtAmt(item.highTopOfPageBidCNY);
|
|
114449
|
+
row.cpcCny = cpcCny;
|
|
114450
|
+
row.bidRangeCny = lowCny === "\u2014" && highCny === "\u2014" ? "\u2014" : `${lowCny} ~ ${highCny}`;
|
|
114451
|
+
}
|
|
114452
|
+
return row;
|
|
114369
114453
|
});
|
|
114370
114454
|
printCliTable(rows, cols);
|
|
114371
|
-
|
|
114455
|
+
if (hasCny) {
|
|
114456
|
+
console.log(` \uFF08USD\u2192CNY \u6C47\u7387 ${usdToCnyRate}\uFF0C\u7F13\u5B58 10 \u5206\u949F\uFF1BJSON \u542B averageCpcCNY \u7B49\u5B57\u6BB5\uFF09
|
|
114457
|
+
`);
|
|
114458
|
+
} else {
|
|
114459
|
+
console.log();
|
|
114460
|
+
}
|
|
114372
114461
|
}
|
|
114373
|
-
function
|
|
114462
|
+
function microsToUSD(v) {
|
|
114374
114463
|
if (v == null) return null;
|
|
114375
114464
|
const n = Number(v);
|
|
114376
114465
|
if (!Number.isFinite(n)) return null;
|
|
114377
114466
|
return Math.round(n / 1e6 * 100) / 100;
|
|
114378
114467
|
}
|
|
114379
|
-
function
|
|
114468
|
+
function legacyUsdAmount(...candidates) {
|
|
114469
|
+
for (const v of candidates) {
|
|
114470
|
+
if (v != null && Number.isFinite(v)) return v;
|
|
114471
|
+
}
|
|
114472
|
+
return null;
|
|
114473
|
+
}
|
|
114474
|
+
function microsItemToUSD(item) {
|
|
114380
114475
|
const {
|
|
114381
114476
|
averageCpc: _legacyCpc,
|
|
114382
114477
|
lowTopOfPageBidMicros: _legacyLow,
|
|
114383
114478
|
highTopOfPageBidMicros: _legacyHigh,
|
|
114479
|
+
averageCpcYuan: _legacyCpcYuan,
|
|
114480
|
+
lowTopOfPageBidYuan: _legacyLowYuan,
|
|
114481
|
+
highTopOfPageBidYuan: _legacyHighYuan,
|
|
114482
|
+
averageCpcUSD: _existingCpcUsd,
|
|
114483
|
+
lowTopOfPageBidUSD: _existingLowUsd,
|
|
114484
|
+
highTopOfPageBidUSD: _existingHighUsd,
|
|
114485
|
+
averageCpcDollar: _legacyCpcDollar,
|
|
114486
|
+
lowTopOfPageBidDollar: _legacyLowDollar,
|
|
114487
|
+
highTopOfPageBidDollar: _legacyHighDollar,
|
|
114488
|
+
averageCpcCny: _dropCny1,
|
|
114489
|
+
lowTopOfPageBidCny: _dropCny2,
|
|
114490
|
+
highTopOfPageBidCny: _dropCny3,
|
|
114491
|
+
averageCpcCNY: _dropCny4,
|
|
114492
|
+
lowTopOfPageBidCNY: _dropCny5,
|
|
114493
|
+
highTopOfPageBidCNY: _dropCny6,
|
|
114384
114494
|
...rest
|
|
114385
114495
|
} = item;
|
|
114386
114496
|
return {
|
|
114387
114497
|
...rest,
|
|
114388
|
-
|
|
114389
|
-
|
|
114390
|
-
|
|
114498
|
+
averageCpcUSD: legacyUsdAmount(
|
|
114499
|
+
microsToUSD(_legacyCpc),
|
|
114500
|
+
_existingCpcUsd,
|
|
114501
|
+
_legacyCpcDollar,
|
|
114502
|
+
_legacyCpcYuan
|
|
114503
|
+
),
|
|
114504
|
+
lowTopOfPageBidUSD: legacyUsdAmount(
|
|
114505
|
+
microsToUSD(_legacyLow),
|
|
114506
|
+
_existingLowUsd,
|
|
114507
|
+
_legacyLowDollar,
|
|
114508
|
+
_legacyLowYuan
|
|
114509
|
+
),
|
|
114510
|
+
highTopOfPageBidUSD: legacyUsdAmount(
|
|
114511
|
+
microsToUSD(_legacyHigh),
|
|
114512
|
+
_existingHighUsd,
|
|
114513
|
+
_legacyHighDollar,
|
|
114514
|
+
_legacyHighYuan
|
|
114515
|
+
)
|
|
114391
114516
|
};
|
|
114392
114517
|
}
|
|
114393
114518
|
function attachBaseCliOptions(cmd) {
|
|
@@ -115575,8 +115700,8 @@ async function runAccountWithdrawSubmit(opts) {
|
|
|
115575
115700
|
});
|
|
115576
115701
|
const feeRateCache = /* @__PURE__ */ new Map();
|
|
115577
115702
|
for (const { currency, availableAmount } of accountDetails) {
|
|
115578
|
-
const
|
|
115579
|
-
if (feeRateCache.has(
|
|
115703
|
+
const cacheKey2 = `${currency}-${availableAmount.toFixed(2)}`;
|
|
115704
|
+
if (feeRateCache.has(cacheKey2)) continue;
|
|
115580
115705
|
try {
|
|
115581
115706
|
const feeParams = new URLSearchParams({
|
|
115582
115707
|
mediaType: "Google",
|
|
@@ -115589,16 +115714,16 @@ async function runAccountWithdrawSubmit(opts) {
|
|
|
115589
115714
|
{},
|
|
115590
115715
|
opts.verbose
|
|
115591
115716
|
);
|
|
115592
|
-
feeRateCache.set(
|
|
115717
|
+
feeRateCache.set(cacheKey2, feeData?.feeRate ?? 0);
|
|
115593
115718
|
} catch {
|
|
115594
115719
|
console.warn(` \u26A0\uFE0F ${currency} \u7BA1\u7406\u8D39\u67E5\u8BE2\u5931\u8D25\uFF0C\u5C06\u4EE5 0 \u8D39\u7387\u63D0\u4EA4\u3002`);
|
|
115595
|
-
feeRateCache.set(
|
|
115720
|
+
feeRateCache.set(cacheKey2, 0);
|
|
115596
115721
|
}
|
|
115597
115722
|
}
|
|
115598
115723
|
const body = accountDetails.map(
|
|
115599
115724
|
({ ma, mai, currency, balance, adjustments, availableAmount }) => {
|
|
115600
|
-
const
|
|
115601
|
-
const feeRate = feeRateCache.get(
|
|
115725
|
+
const cacheKey2 = `${currency}-${availableAmount.toFixed(2)}`;
|
|
115726
|
+
const feeRate = feeRateCache.get(cacheKey2) ?? 0;
|
|
115602
115727
|
const taxRate = currency === "CNY" ? 0.06 : 0;
|
|
115603
115728
|
const totalAmounts = availableAmount * (1 + feeRate) * (1 + taxRate);
|
|
115604
115729
|
return {
|
package/dist/skill/SKILL.md
CHANGED
|
@@ -148,11 +148,11 @@ Windows 注意:部分 Agent 客户端通过 PowerShell / cmd 代执行命令
|
|
|
148
148
|
|
|
149
149
|
### 金额与品牌名
|
|
150
150
|
|
|
151
|
-
- **金额单位统一为「元」**:CLI 出口的所有 JSON /
|
|
151
|
+
- **金额单位统一为「元」**:CLI 出口的所有 JSON / 表格里,账户/投放类金额字段一般以**元**为单位(如 `budgetAmountYuan`、`maxCPCAmountYuan`)。报告/表格金额保留 2 位小数,格式示例:`¥50.00 CNY`、`$50.00 USD`(符号与 `currencyCode` 一致)。
|
|
152
152
|
- **`ad campaigns --json`/`--json-out`**:列表里的 `budget` 已是主币种「元」(与 `ad campaign-edit --budget` 写参同口径),可直接作用户可见日预算。
|
|
153
153
|
- **`ad groups --json`**:日预算/CPA 读 `maxCPCAmountYuan` / `targetCpaAmountYuan`(元)。
|
|
154
154
|
- **`google-analysis` 落盘 `campaigns-*.json`**:日预算读 `budgetAmountYuan`(元),系列目标 CPA 读 `campaignTargetCpaYuan` / `maximizeConversionsTargetCpaYuan`(元);同文件 `spend` / `averageCpc` / `costPerConversion` 也是元。
|
|
155
|
-
- **`keyword --json
|
|
155
|
+
- **`keyword --json`**:美元出价读 `averageCpcUSD` / `lowTopOfPageBidUSD` / `highTopOfPageBidUSD`;人民币读 `averageCpcCNY` / `lowTopOfPageBidCNY` / `highTopOfPageBidCNY`(根级 `usdToCnyExchangeRate`)。根级 `bidAmountCurrency: "USD"`。限定市场:`keyword geo-list` + `--geo 2840,2826`。
|
|
156
156
|
- **品牌名**必须来自(按优先级):(1) 用户明确提供 (2) `list-accounts` 返回的 `mag.advertiserName` (3) 用户提供的网址→域名占位并标注 `[待确认品牌名]`。**严禁**把英文域名自行翻译为虚构中文品牌。
|
|
157
157
|
|
|
158
158
|
### 批量任务硬约束
|
package/dist/skill/_meta.json
CHANGED
|
@@ -83,7 +83,7 @@ CLI 出口的所有 JSON / 表格金额已统一为**元**,关键字段:
|
|
|
83
83
|
| `ad campaigns --json` | `budget`(元,与 `--budget` 写参同口径) |
|
|
84
84
|
| `ad groups --json` | `maxCPCAmountYuan`、`targetCpaAmountYuan` |
|
|
85
85
|
| `google-analysis campaigns` 落盘 `campaigns-*.json` | `budgetAmountYuan`、`campaignTargetCpaYuan`、`maximizeConversionsTargetCpaYuan`;同行 `spend` / `averageCpc` / `costPerConversion` 也是元 |
|
|
86
|
-
| `keyword suggest --json` | `
|
|
86
|
+
| `keyword suggest --json` | `averageCpcUSD`、`lowTopOfPageBidUSD`、`highTopOfPageBidUSD`;`averageCpcCNY` |
|
|
87
87
|
| `balance` 等账户余额接口 | `remainingAccountBudget`(元) |
|
|
88
88
|
|
|
89
89
|
旧字段 `budgetAmount`(分)、`maxCPCAmountDisplay`、`*Micros`(微元)**已不再落盘**,下游脚本无需做单位换算。金额保留 2 位小数,带货币代码(如 `¥50.00 CNY`、`$50.00 USD`),`currencyCode` 从响应读取,跨币种账户分表;细则见 `references/currency.md`。
|
|
@@ -46,7 +46,7 @@
|
|
|
46
46
|
|
|
47
47
|
## 金额单位约定
|
|
48
48
|
|
|
49
|
-
- **CLI
|
|
49
|
+
- **CLI 出口的大多数 JSON / 表格金额以「元」为单位**:`budget`、`*Yuan` 后缀(`budgetAmountYuan`、`maxCPCAmountYuan` 等)、`spend` / `averageCpc` / `costPerConversion` 等。**例外**:`keyword suggest` 的 Planner 出价为美元,字段名为 `averageCpcUSD` / `lowTopOfPageBidUSD` / `highTopOfPageBidUSD`,并可选附带 `averageCpcCNY` 等换算值。
|
|
50
50
|
- **写 CLI 参数**(`--budget`、`--max-cpc`、`--target-cpa`、`--amount` 等):同样传**主币种元**,与账户 `currencyCode` 一致;CLI 内部按需 ×100 / ×1_000_000 写后端。
|
|
51
51
|
- 旧版网关字段(`budgetAmount` 分、`*Micros` 微元、`maxCPCAmountDisplay` 等)**已不再落盘到 CLI 输出**,下游脚本无需做单位换算。
|
|
52
52
|
|
|
@@ -70,7 +70,7 @@
|
|
|
70
70
|
|
|
71
71
|
`class` 取值:`scenario` | `geo` | `tech` | `question`。
|
|
72
72
|
|
|
73
|
-
拓词编排见 `references/keyword-planner-workflows.md`;Planner
|
|
73
|
+
拓词编排见 `references/keyword-planner-workflows.md`;Planner 出价用 `*USD` 与 `*CNY`,根级 `bidAmountCurrency` / `usdToCnyExchangeRate`。
|
|
74
74
|
|
|
75
75
|
---
|
|
76
76
|
|
|
@@ -3,8 +3,7 @@
|
|
|
3
3
|
> **拓词命令**读本文;**建户 JSON + validate + create** 读 `references/google-ads-campaign-plan.md`。关键词数量规则:`google-ads-rules/google-ads-keyword-taxonomy.md`。
|
|
4
4
|
>
|
|
5
5
|
> **数据口径**:`siluzan-tso keyword` 走网关 `keywordidea/google`(Google Keyword Planner 市场指标);可选 `--url` 叠加 **网址拓词**(`websitereco`,非 Google API)。与账户内 **`google-analysis` 投放表现**不是同一套数据,文档与回复中须区分「市场参考」与「账户实际花费/转化」。
|
|
6
|
-
|
|
7
|
-
> 最终如果用户要求输出excel, 金额相关的表头需要注明是美元($)或人民币(¥)
|
|
6
|
+
|
|
8
7
|
---
|
|
9
8
|
|
|
10
9
|
## 路径选择(拓词前先定分支)
|
|
@@ -23,7 +22,7 @@
|
|
|
23
22
|
|
|
24
23
|
1. (可选)宿主 **联网搜索** 或 `rag query` 归纳 **2–8 个英文种子词**(RAG 见 §0,联网搜索勿与 Google 指标混写在同一列)。
|
|
25
24
|
2. `siluzan-tso keyword -k "种子1,种子2,..." [--url "<落地页或竞品站>"] --json-out ./snap-kw`
|
|
26
|
-
3. 对落盘 `items`:以 **`keyword` 返回行为准**(有 `montlySearch` / `
|
|
25
|
+
3. 对落盘 `items`:以 **`keyword` 返回行为准**(有 `montlySearch` / `averageCpcUSD` 等);联网搜索得到的词若无对应行,可并入词包但须标注 **「无 Google 市场数据」**,或丢弃。
|
|
27
26
|
|
|
28
27
|
### 分支 B — 仅 Google 市场数据(默认)
|
|
29
28
|
|
|
@@ -159,7 +158,7 @@ siluzan-tso keyword -k "structural adhesive,SG-200,curtain wall bonding" --url "
|
|
|
159
158
|
### 5)高商业意图粗筛(出价 + 竞争度 + 搜索量)
|
|
160
159
|
|
|
161
160
|
1. `siluzan-tso keyword -k "..." --json-out ./snap-kw`
|
|
162
|
-
2. 对落盘 `items` 按 `
|
|
161
|
+
2. 对落盘 `items` 按 `averageCpcUSD`(CLI 出口已 ÷1,000,000,货币见根级 **`bidAmountCurrency: "USD"`**)、`competition` / `competitionV2`、`montlySearch` 综合排序截断;此外 `lowTopOfPageBidUSD` / `highTopOfPageBidUSD`(页首出价 20/80 分位,**美元**)可用于评估合理出价区间。
|
|
163
162
|
|
|
164
163
|
### 6)词包 → campaign-create JSON
|
|
165
164
|
|
|
@@ -183,6 +182,6 @@ siluzan-tso keyword geo-list [--country-code <codes>] [--name-contains <text>] [
|
|
|
183
182
|
|
|
184
183
|
`--google-only`:只调 `keywordidea/google`,不叠加 `--url` 的网址拓词;**分支 B(仅 Google)必加**。
|
|
185
184
|
|
|
186
|
-
**返回字段**(与后端 `Samm.Core.Service.KeywordRecommendation` 对齐):根级 `bidAmountCurrency: "USD"
|
|
185
|
+
**返回字段**(与后端 `Samm.Core.Service.KeywordRecommendation` 对齐):根级 `bidAmountCurrency: "USD"`、`usdToCnyExchangeRate`(可选);每条 `averageCpcUSD` / `lowTopOfPageBidUSD` / `highTopOfPageBidUSD`(**美元**)及对应 `averageCpcCNY` / `lowTopOfPageBidCNY` / `highTopOfPageBidCNY`(CLI 换算);另有 `keyword` / `montlySearch` / `competition` / `competitionV2` / `source` 等。金额已由网关微元 ÷1,000,000。
|
|
187
186
|
|
|
188
187
|
与只读账户关键词列表、否词 CRUD 的对照仍归 **`references/google-ads.md`** 中 `ad keywords` / `ad keyword-*` 各节。
|
|
@@ -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.20-beta.
|
|
12
|
+
$PKG_VERSION = '1.1.20-beta.15'
|
|
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.20-beta.
|
|
12
|
+
readonly PKG_VERSION="1.1.20-beta.15"
|
|
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"
|