siluzan-tso-cli 1.1.22-beta.10 → 1.1.22-beta.11
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 +90 -2
- package/dist/skill/_meta.json +2 -2
- package/dist/skill/references/analytics/account-analytics.md +25 -1
- package/dist/skill/references/analytics/keyword-planner-workflows.md +2 -2
- 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.22-beta.
|
|
54
|
+
> **注意**:当前为测试版(1.1.22-beta.11),供内部测试使用。正式发布后安装命令将改为 `npm install -g siluzan-tso-cli`。
|
|
55
55
|
|
|
56
56
|
| 助手 | 建议 `--ai` |
|
|
57
57
|
| ----------------------- | ------------------------------------ |
|
package/dist/index.js
CHANGED
|
@@ -4623,7 +4623,10 @@ function buildOutlineHints(section) {
|
|
|
4623
4623
|
hints.push(...GOOGLE_ANALYSIS_RATE_NORMALIZED_OUTLINE_HINTS);
|
|
4624
4624
|
}
|
|
4625
4625
|
if (section === "campaigns") {
|
|
4626
|
-
hints.push(
|
|
4626
|
+
hints.push(
|
|
4627
|
+
...GOOGLE_ANALYSIS_CAMPAIGNS_OUTLINE_BUDGET_HINTS,
|
|
4628
|
+
...GOOGLE_ANALYSIS_CAMPAIGNS_COMPETITIVE_METRICS_HINTS
|
|
4629
|
+
);
|
|
4627
4630
|
}
|
|
4628
4631
|
const zhHints = GOOGLE_ANALYSIS_ZH_FIELD_HINTS_BY_SECTION[section];
|
|
4629
4632
|
if (zhHints) hints.push(...zhHints);
|
|
@@ -4732,7 +4735,7 @@ async function writeGoogleAnalysisSnapshot(params) {
|
|
|
4732
4735
|
agentHint: OUTLINE_AGENT_HINT
|
|
4733
4736
|
};
|
|
4734
4737
|
}
|
|
4735
|
-
var LEGACY_MANIFEST_FILE, CLI_PACKAGE2, SCHEMA_VERSION2, DEFAULT_FIELD_GUIDE2, GOOGLE_ANALYSIS_CAMPAIGNS_OUTLINE_BUDGET_HINTS, RATE_BEARING_SECTIONS, GOOGLE_ANALYSIS_RATE_NORMALIZED_OUTLINE_HINTS, GOOGLE_ANALYSIS_ZH_FIELD_HINTS_BY_SECTION;
|
|
4738
|
+
var LEGACY_MANIFEST_FILE, CLI_PACKAGE2, SCHEMA_VERSION2, DEFAULT_FIELD_GUIDE2, GOOGLE_ANALYSIS_CAMPAIGNS_OUTLINE_BUDGET_HINTS, GOOGLE_ANALYSIS_CAMPAIGNS_COMPETITIVE_METRICS_HINTS, RATE_BEARING_SECTIONS, GOOGLE_ANALYSIS_RATE_NORMALIZED_OUTLINE_HINTS, GOOGLE_ANALYSIS_ZH_FIELD_HINTS_BY_SECTION;
|
|
4736
4739
|
var init_google_analysis = __esm({
|
|
4737
4740
|
"src/utils/snapshot/google-analysis.ts"() {
|
|
4738
4741
|
"use strict";
|
|
@@ -4749,6 +4752,10 @@ var init_google_analysis = __esm({
|
|
|
4749
4752
|
"// \u91D1\u989D\uFF1A`campaigns[].budgetAmountYuan` / `campaignTargetCpaYuan` / `maximizeConversionsTargetCpaYuan` \u5747\u4E3A**\u5143**\uFF08CLI \u51FA\u53E3\u7EDF\u4E00\uFF09\uFF1B\u540C\u884C `spend` / `averageCpc` / `costPerConversion` \u4E5F\u662F\u5143\u3002",
|
|
4750
4753
|
"// \u65E7\u5B57\u6BB5 `budgetAmount`\uFF08\u5206\uFF09/ `*Micros`\uFF08\u5FAE\u5143\uFF09\u5DF2\u4E0D\u518D\u843D\u76D8\uFF0C**\u7981\u6B62**\u518D\u505A \xF7100 / \xF71_000_000 \u6362\u7B97\u3002"
|
|
4751
4754
|
];
|
|
4755
|
+
GOOGLE_ANALYSIS_CAMPAIGNS_COMPETITIVE_METRICS_HINTS = [
|
|
4756
|
+
"// \u7ADE\u4E89\u6307\u6807\uFF1A`campaigns[].competitiveMetrics` \u4E3A Google \u539F\u751F **0~1 \u5C0F\u6570**\uFF08\u5982 `0.0999` = 9.99%\uFF09\uFF0C\u542B Top/AbsoluteTop/Content/ClickShare \u7B49 15 \u9879\uFF1B\u65E0 GAQL \u7ADE\u4E89\u6570\u636E\u65F6\u4E3A `null`\u3002",
|
|
4757
|
+
"// \u884C\u9876 `searchImpressionShare` / `searchBudgetLostImpressionShare` / `searchRankLostImpressionShare` \u4ECD\u4E3A **0~100 \u767E\u5206\u6570**\uFF08\u4EC5 3 \u9879\uFF09\uFF1B**\u7981\u6B62**\u4E0E `competitiveMetrics.*` \u76F8\u52A0\u6216\u6DF7\u7B97\u3002"
|
|
4758
|
+
];
|
|
4752
4759
|
RATE_BEARING_SECTIONS = /* @__PURE__ */ new Set([
|
|
4753
4760
|
"overview",
|
|
4754
4761
|
"keywords",
|
|
@@ -116564,6 +116571,62 @@ init_auth();
|
|
|
116564
116571
|
init_cli_json_snapshot();
|
|
116565
116572
|
init_cli_table();
|
|
116566
116573
|
|
|
116574
|
+
// src/commands/keyword-account-match.ts
|
|
116575
|
+
init_auth();
|
|
116576
|
+
function normalizeKeywordForAccountMatch(raw) {
|
|
116577
|
+
return unwrapKeywordDisplayTextForEdit(raw.trim()).toLowerCase();
|
|
116578
|
+
}
|
|
116579
|
+
function extractKeywordTextFromManagementRow(row) {
|
|
116580
|
+
const kt = row["keywordText"];
|
|
116581
|
+
if (Array.isArray(kt) && kt.length > 0 && typeof kt[0] === "string") {
|
|
116582
|
+
return kt[0];
|
|
116583
|
+
}
|
|
116584
|
+
const t = row["text"];
|
|
116585
|
+
if (typeof t === "string") return t;
|
|
116586
|
+
const kw = row["keyword"];
|
|
116587
|
+
if (typeof kw === "string") return kw;
|
|
116588
|
+
return "";
|
|
116589
|
+
}
|
|
116590
|
+
async function fetchAccountPositiveKeywordNormSet(config, googleApiUrl, accountId, opts) {
|
|
116591
|
+
const lookback = opts?.lookbackDays ?? 3650;
|
|
116592
|
+
const params = new URLSearchParams();
|
|
116593
|
+
params.set("startDate", toGoogleDate(void 0, -lookback));
|
|
116594
|
+
params.set("endDate", toGoogleDate(void 0, 0));
|
|
116595
|
+
const url = `${googleApiUrl}/keywordmanagement/v2/list/${accountId}?${params}`;
|
|
116596
|
+
const data = await apiFetch2(
|
|
116597
|
+
url,
|
|
116598
|
+
config,
|
|
116599
|
+
{},
|
|
116600
|
+
opts?.verbose
|
|
116601
|
+
);
|
|
116602
|
+
const rawItems = data.data ?? [];
|
|
116603
|
+
const seenIds = /* @__PURE__ */ new Set();
|
|
116604
|
+
const normSet = /* @__PURE__ */ new Set();
|
|
116605
|
+
for (const row of rawItems) {
|
|
116606
|
+
const id = row["id"];
|
|
116607
|
+
if (id !== void 0) {
|
|
116608
|
+
if (seenIds.has(id)) continue;
|
|
116609
|
+
seenIds.add(id);
|
|
116610
|
+
}
|
|
116611
|
+
const text = extractKeywordTextFromManagementRow(row);
|
|
116612
|
+
if (!text) continue;
|
|
116613
|
+
const norm = normalizeKeywordForAccountMatch(text);
|
|
116614
|
+
if (norm) normSet.add(norm);
|
|
116615
|
+
}
|
|
116616
|
+
return normSet;
|
|
116617
|
+
}
|
|
116618
|
+
function keywordAlreadyInAccountFlag(recommendKeyword, accountNormSet) {
|
|
116619
|
+
const norm = normalizeKeywordForAccountMatch(recommendKeyword ?? "");
|
|
116620
|
+
if (!norm) return 0;
|
|
116621
|
+
return accountNormSet.has(norm) ? 1 : 0;
|
|
116622
|
+
}
|
|
116623
|
+
function annotateKeywordSuggestWithAccountPresence(items, accountNormSet) {
|
|
116624
|
+
return items.map((item) => {
|
|
116625
|
+
const flag = keywordAlreadyInAccountFlag(item.keyword, accountNormSet);
|
|
116626
|
+
return { ...item, alreadyInAccount: flag };
|
|
116627
|
+
});
|
|
116628
|
+
}
|
|
116629
|
+
|
|
116567
116630
|
// src/commands/keyword-geo.ts
|
|
116568
116631
|
function parseGeoTargetConstantIds(raw) {
|
|
116569
116632
|
if (!raw?.trim()) return [];
|
|
@@ -116828,6 +116891,27 @@ async function runKeywordSuggest(opts) {
|
|
|
116828
116891
|
return !excludes.some((exc) => kw.includes(exc));
|
|
116829
116892
|
});
|
|
116830
116893
|
}
|
|
116894
|
+
if (accountId) {
|
|
116895
|
+
try {
|
|
116896
|
+
const accountNormSet = await fetchAccountPositiveKeywordNormSet(
|
|
116897
|
+
config,
|
|
116898
|
+
googleApiUrl,
|
|
116899
|
+
accountId,
|
|
116900
|
+
{ verbose: opts.verbose }
|
|
116901
|
+
);
|
|
116902
|
+
items = annotateKeywordSuggestWithAccountPresence(items, accountNormSet);
|
|
116903
|
+
if (opts.verbose) {
|
|
116904
|
+
const matched = items.filter((r) => r.alreadyInAccount === 1).length;
|
|
116905
|
+
console.error(
|
|
116906
|
+
` [keyword] \u8D26\u6237\u8BCD\u5E93 ${accountNormSet.size} \u4E2A\u8BCD\u5E72\uFF1B\u63A8\u8350 ${items.length} \u6761\uFF0C\u5176\u4E2D\u5DF2\u5728\u8D26\u6237 ${matched} \u6761`
|
|
116907
|
+
);
|
|
116908
|
+
}
|
|
116909
|
+
} catch (err) {
|
|
116910
|
+
console.error(
|
|
116911
|
+
` [keyword] \u8B66\u544A\uFF1A\u62C9\u53D6\u8D26\u6237\u5173\u952E\u8BCD\u5931\u8D25\uFF0C\u8DF3\u8FC7 alreadyInAccount \u6807\u6CE8\uFF1A${err instanceof Error ? err.message : String(err)}`
|
|
116912
|
+
);
|
|
116913
|
+
}
|
|
116914
|
+
}
|
|
116831
116915
|
const n = items.length;
|
|
116832
116916
|
const kwPayload = buildKeywordSuggestJsonPayload(items, { bidAmountCurrency });
|
|
116833
116917
|
const displayItems = kwPayload.items;
|
|
@@ -116849,6 +116933,7 @@ async function runKeywordSuggest(opts) {
|
|
|
116849
116933
|
const cur = bidAmountCurrency;
|
|
116850
116934
|
const cols = [
|
|
116851
116935
|
{ key: "keyword", header: "\u5173\u952E\u8BCD" },
|
|
116936
|
+
...accountId ? [{ key: "alreadyInAccount", header: "\u5DF2\u5728\u8D26\u6237\u4E2D" }] : [],
|
|
116852
116937
|
{ key: "montlySearch", header: "\u6708\u5747\u641C\u7D22" },
|
|
116853
116938
|
{ key: "cpcMain", header: `\u5E73\u5747CPC(${cur})` },
|
|
116854
116939
|
{ key: "bidRangeMain", header: `\u9875\u9996\u51FA\u4EF7(${cur})` },
|
|
@@ -116862,6 +116947,9 @@ async function runKeywordSuggest(opts) {
|
|
|
116862
116947
|
const competitionDisplay = item.competitionV2 ?? (item.competition != null ? item.competition.toFixed(2) : "\u2014");
|
|
116863
116948
|
return {
|
|
116864
116949
|
keyword: item.keyword ?? "",
|
|
116950
|
+
...accountId ? {
|
|
116951
|
+
alreadyInAccount: item.alreadyInAccount === 1 ? "1" : item.alreadyInAccount === 0 ? "0" : "\u2014"
|
|
116952
|
+
} : {},
|
|
116865
116953
|
montlySearch: String(item.montlySearch ?? "\u2014"),
|
|
116866
116954
|
cpcMain,
|
|
116867
116955
|
bidRangeMain,
|
package/dist/skill/_meta.json
CHANGED
|
@@ -215,7 +215,31 @@ CLI 在落盘前为以下维度自动补「中文译名字段」(**原英文
|
|
|
215
215
|
|
|
216
216
|
`campaigns[]` 每行额外包含:`conversionsValue`、`conversionsValuePerCost`(`spend ≤ 0` 时为 0)、`campaignTargetCpaYuan`、`maximizeConversionsTargetCpaYuan`、`manualCpcEnhancedCpcEnabled`、`percentCpcEnhancedCpcEnabled`。所有金额字段(`*Yuan` 后缀)已统一为元,可直接展示,无需换算。
|
|
217
217
|
|
|
218
|
-
日预算字段为 `budgetAmountYuan`(元),脚本可直接 `row.
|
|
218
|
+
日预算字段为 `budgetAmountYuan`(元),脚本可直接 `row.budgetAmountYuan`;网关原始 `budgetAmount`(分)已不再落盘。
|
|
219
|
+
|
|
220
|
+
#### 竞争指标 `competitiveMetrics`(推荐)
|
|
221
|
+
|
|
222
|
+
网关 `CampaignSectionData` 每系列可带嵌套对象 `competitiveMetrics`(对齐 Sammamish `ReportCompetitiveMetrics`),**值为 Google 原生 0~1 小数**(如 `0.0999` 表示约 9.99%)。常见字段:
|
|
223
|
+
|
|
224
|
+
| 字段 | 含义 |
|
|
225
|
+
|------|------|
|
|
226
|
+
| `searchImpressionShare` | 搜索展示份额 |
|
|
227
|
+
| `searchTopImpressionShare` | 搜索页首展示份额 |
|
|
228
|
+
| `searchAbsoluteTopImpressionShare` | 搜索绝对页首展示份额 |
|
|
229
|
+
| `searchBudgetLostImpressionShare` | 因预算丢失的搜索展示份额 |
|
|
230
|
+
| `searchRankLostImpressionShare` | 因排名丢失的搜索展示份额 |
|
|
231
|
+
| `searchBudgetLostTopImpressionShare` / `searchRankLostTopImpressionShare` | 页首维度预算/排名丢失 |
|
|
232
|
+
| `searchBudgetLostAbsoluteTopImpressionShare` / `searchRankLostAbsoluteTopImpressionShare` | 绝对页首维度预算/排名丢失 |
|
|
233
|
+
| `searchExactMatchImpressionShare` | 完全匹配展示份额 |
|
|
234
|
+
| `contentImpressionShare` / `contentBudgetLostImpressionShare` / `contentRankLostImpressionShare` | 展示网络份额与丢失 |
|
|
235
|
+
| `searchClickShare` | 搜索点击份额 |
|
|
236
|
+
| `relativeCtr` | 相对点击率 |
|
|
237
|
+
|
|
238
|
+
无竞争数据时 `competitiveMetrics` 为 `null`(或缺省)。写 Excel「x%」列:`(v * 100).toFixed(2) + '%'`。
|
|
239
|
+
|
|
240
|
+
#### 行顶 legacy 份额(勿与 `competitiveMetrics` 混用)
|
|
241
|
+
|
|
242
|
+
同一行仍保留 3 个行顶字段 `searchImpressionShare`、`searchBudgetLostImpressionShare`、`searchRankLostImpressionShare`,口径为 **0~100 百分数**(网关已 ×100)。分析 Top/AbsoluteTop/Content/ClickShare 等请用 `competitiveMetrics`;仅用行顶 3 项时按百分数理解,**禁止**与 `competitiveMetrics.*` 相加。
|
|
219
243
|
|
|
220
244
|
### campaign-hour 字段
|
|
221
245
|
|
|
@@ -201,10 +201,10 @@ siluzan-tso keyword -k "<必填,逗号分隔多词>" [-a <mediaCustomerId>] [-
|
|
|
201
201
|
siluzan-tso keyword geo-list [--country-code <codes>] [--name-contains <text>] [--json-out ./snap] [--json-out <dir>]
|
|
202
202
|
```
|
|
203
203
|
|
|
204
|
-
`-a`:走 `keywordrecommendation/recommend/{id}/google`;先 `list-accounts -m Google -k <id>` 确认账户与 **`currencyCode`**;出价金额字段为 `averageCpc` / `lowTopOfPageBid` / `highTopOfPageBid`(**账户币种「元」**,非汇率换算)。
|
|
204
|
+
`-a`:走 `keywordrecommendation/recommend/{id}/google`;先 `list-accounts -m Google -k <id>` 确认账户与 **`currencyCode`**;出价金额字段为 `averageCpc` / `lowTopOfPageBid` / `highTopOfPageBid`(**账户币种「元」**,非汇率换算)。CLI 会再调 `keywordmanagement/v2/list`(约 10 年区间)拉账户正向词,对每条推荐写 **`alreadyInAccount`**:`1`=词干已存在,`0`=否(匹配时忽略 broad/phrase/exact 的 `""`/`[]` 包裹,大小写不敏感)。该列表为报表口径,非 Google 后台 criterion 全量;区间内从未出报表行的词可能标 `0`。
|
|
205
205
|
|
|
206
206
|
`--google-only`:只调 Google 推荐主接口(有 `-a` 为账户接口,无 `-a` 为 `keywordidea/google`),不叠加 `--url` 的网址拓词;**分支 B(仅 Google)必加**。
|
|
207
207
|
|
|
208
|
-
**返回字段**(与后端 `Samm.Core.Service.KeywordRecommendation` 对齐):根级与每条 **`bidAmountCurrency`**(无账户=`USD`;有账户=`list-accounts` 的 `currencyCode`);`averageCpc` / `lowTopOfPageBid` / `highTopOfPageBid`(微元 ÷1,000,000,**与 `bidAmountCurrency` 一致**)。另有 `keyword` / `montlySearch` / `competition` / `competitionV2` / `source`
|
|
208
|
+
**返回字段**(与后端 `Samm.Core.Service.KeywordRecommendation` 对齐):根级与每条 **`bidAmountCurrency`**(无账户=`USD`;有账户=`list-accounts` 的 `currencyCode`);`averageCpc` / `lowTopOfPageBid` / `highTopOfPageBid`(微元 ÷1,000,000,**与 `bidAmountCurrency` 一致**)。另有 `keyword` / `montlySearch` / `monthlySearchVolumes` / `competition` / `competitionV2` / `source` 等;**有 `-a` 时**每条多 **`alreadyInAccount`**(`0` | `1`)。
|
|
209
209
|
|
|
210
210
|
与只读账户关键词列表、否词 CRUD 的对照仍归 **`references/google-ads/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.22-beta.
|
|
12
|
+
$PKG_VERSION = '1.1.22-beta.11'
|
|
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.22-beta.
|
|
12
|
+
readonly PKG_VERSION="1.1.22-beta.11"
|
|
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"
|