siluzan-tso-cli 1.1.22-beta.10 → 1.1.22-beta.12

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.
Files changed (31) hide show
  1. package/README.md +1 -1
  2. package/dist/index.js +90 -2
  3. package/dist/skill/_meta.json +2 -2
  4. package/dist/skill/assets/campaign-create-template.md +12 -12
  5. package/dist/skill/assets/pmax-create-template.md +2 -2
  6. package/dist/skill/references/README.md +1 -1
  7. package/dist/skill/references/accounts/accounts.md +14 -26
  8. package/dist/skill/references/accounts/finance.md +5 -7
  9. package/dist/skill/references/accounts/open-account-by-media.md +2 -3
  10. package/dist/skill/references/accounts/open-account-google-ui.md +43 -74
  11. package/dist/skill/references/analytics/account-analytics.md +25 -1
  12. package/dist/skill/references/analytics/keyword-planner-workflows.md +5 -5
  13. package/dist/skill/references/analytics/rag.md +1 -1
  14. package/dist/skill/references/analytics/reporting.md +8 -15
  15. package/dist/skill/references/core/setup.md +1 -1
  16. package/dist/skill/references/core/workflows.md +19 -41
  17. package/dist/skill/references/google-ads/google-ads.md +6 -12
  18. package/dist/skill/references/google-ads/pmax-api.md +35 -41
  19. package/dist/skill/references/misc/tso-home.md +20 -84
  20. package/dist/skill/references/operations/clue.md +1 -1
  21. package/dist/skill/references/operations/forewarning.md +0 -2
  22. package/dist/skill/references/operations/optimize.md +2 -4
  23. package/dist/skill/report-templates/README.md +1 -1
  24. package/dist/skill/report-templates/bing-period-report.md +2 -3
  25. package/dist/skill/report-templates/google-ads-diagnosis.md +1 -1
  26. package/dist/skill/report-templates/google-inquiry-analysis.md +1 -1
  27. package/dist/skill/report-templates/meta-period-report.md +2 -3
  28. package/dist/skill/report-templates/tiktok-period-report.md +4 -7
  29. package/dist/skill/scripts/install.ps1 +1 -1
  30. package/dist/skill/scripts/install.sh +1 -1
  31. 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.10),供内部测试使用。正式发布后安装命令将改为 `npm install -g siluzan-tso-cli`。
54
+ > **注意**:当前为测试版(1.1.22-beta.12),供内部测试使用。正式发布后安装命令将改为 `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(...GOOGLE_ANALYSIS_CAMPAIGNS_OUTLINE_BUDGET_HINTS);
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,
@@ -1,5 +1,5 @@
1
1
  {
2
2
  "slug": "siluzan-tso",
3
- "version": "1.1.22-beta.10",
4
- "publishedAt": 1779948041465
3
+ "version": "1.1.22-beta.12",
4
+ "publishedAt": 1779959114618
5
5
  }
@@ -7,7 +7,7 @@
7
7
  **`ad campaign-create` 提交前**,CLI 在 JSON 原文之外额外处理(不影响 validate 读到的「元」口径):
8
8
 
9
9
  1. 剥除以 `_` 开头的注解键(如 `_meta`、`_comment_budget`、`_comment_finalurl`);
10
- 2. 外层 body:`account` → 数字 `customerId`;补全 `KeywordRecommendationsV2`(按广告组名,`Value` 可为 `[]`);`googleDataRecordId` 缺省为 `""`(与 Web 智投一致);
10
+ 2. 外层 body:`account` → 数字 `customerId`;补全 `KeywordRecommendationsV2`(按广告组名,`Value` 可为 `[]`);`googleDataRecordId` 缺省为 `""`(CLI 默认);
11
11
  3. `campaign` 金额字段「元」→「分」(×100);
12
12
  4. `ExtensionsForBatchJob` 中 SITELINK 的 `Properties` 规范化(见下文「SITELINK」)。
13
13
 
@@ -99,11 +99,11 @@ siluzan-tso ad batch diff --batch-id <taskId> --config-file ./campaign.json --js
99
99
  | `url` | string | | 智投展示用 URL;后端只读,用于回显 |
100
100
  | `locations` | string[] | | 展示用地区名(后端只读,可空数组) |
101
101
  | `productWords` | string[] | | 智投/推荐用产品核心词 |
102
- | `googleDataRecordId` | string \| null | | 智投记录 ID;省略时提交 `""`(与 Web 智投一致) |
102
+ | `googleDataRecordId` | string \| null | | 智投记录 ID;省略时提交 `""`(CLI 默认) |
103
103
  | `draft` | boolean | | `false`(默认)立即发布到 Google;`true` 仅保存草稿,需后续 `ad batch publish` |
104
104
  | `campaign` | object | ✅ | 内层 Campaign 对象,见下表 |
105
105
 
106
- > 提交时 CLI 另附 `KeywordRecommendationsV2`:`[{ Key: <广告组 Name>, Value: [] }, …]`,与 Web `/advertising/AICreation` 结构一致;JSON 文件内无需手写。
106
+ > 提交时 CLI 另附 `KeywordRecommendationsV2`:`[{ Key: <广告组 Name>, Value: [] }, …]`,由 CLI 自动附加;JSON 文件内无需手写。
107
107
 
108
108
  ---
109
109
 
@@ -202,7 +202,7 @@ siluzan-tso ad batch diff --batch-id <taskId> --config-file ./campaign.json --js
202
202
 
203
203
  ### 关键词块(`KeywordsForBatchJob[j]`)
204
204
 
205
- 每个块描述一组**同匹配类型**的关键词。若同一块内混用裸词 / `"词组"` / `[完全]`(或与块级 `MatchTypeV2` 冲突的符号),`campaign-validate` 会**自动拆成多个块**(顺序:BROAD → PHRASE → EXACT,与 Web 智投一致),再按块级类型修复词面。
205
+ 每个块描述一组**同匹配类型**的关键词。若同一块内混用裸词 / `"词组"` / `[完全]`(或与块级 `MatchTypeV2` 冲突的符号),`campaign-validate` 会**自动拆成多个块**(顺序:BROAD → PHRASE → EXACT),再按块级类型修复词面。
206
206
 
207
207
  | 字段 | 类型 | 说明 |
208
208
  | ------------- | --------------------------------- | --------------------------------------------------------------------- |
@@ -217,7 +217,7 @@ siluzan-tso ad batch diff --batch-id <taskId> --config-file ./campaign.json --js
217
217
  | `TypeV2` | string | | RSA 填 `RESPONSIVE_SEARCH_AD` |
218
218
  | `DestinationUrl` | string | ✅ | 展示/编辑用落地页 URL |
219
219
  | `Finalurl` | string | ✅ | **后端 BatchJob 必填**;与 `DestinationUrl` 填**相同** URL。勿只写前者——validate 可能仍通过,create 会失败 |
220
- | `AdTitle` | null \| string | | 可选;无标题时写 `null`(与 Web 智投一致) |
220
+ | `AdTitle` | null \| string | | 可选;无标题时写 `null`(CLI 默认) |
221
221
  | `Path1` / `Path2` | string | ✅ | 显示路径(**必填**,缺/null 会导致后端 BatchJob `ArgumentNullException`);**≤ 15 字符**(CJK 按 2 计);小写 a-z、数字、连字符 |
222
222
  | `headlinePart1/2/3` | string | ✅ | 前 3 条标题;**每条 ≤ 30 字符**(CJK 按 2 计) |
223
223
  | `AddtionalHeadlines` | string[] | | 第 4–15 条标题(合计 ≤ 15) |
@@ -226,13 +226,13 @@ siluzan-tso ad batch diff --batch-id <taskId> --config-file ./campaign.json --js
226
226
 
227
227
  ---
228
228
 
229
- ## 校验规则(CLI 镜像后端)
229
+ ## 校验规则(`ad campaign-validate`)
230
230
 
231
- | 来源 | 规则 |
232
- | ------------------------------------------------------------------------------------------ | --------------------------------------------------------------------------------------------------- |
233
- | `CampaignCommandController.CreateCampaignAsync` | `customerName` 非空 / `campaign` 非空 |
234
- | `CampaignCommandController` 行 94–106 | `campaign.TargetPartnerSearchNetwork` 必须 false;`!TargetGoogleSearch && TargetSearchNetwork` 拒绝 |
235
- | Google Ads BatchJob | RSA `Path1`/`Path2` 必填(缺/null → 后端 ArgumentNullException);字段数与字符上限;关键词词面非空;SITELINK `Line2`/`Line3` ≤25 字且不可 null |
236
- | CLI 实务 | `Budget > 0`、地理/语言至少 1 项、日期格式与先后、出价策略与配套字段 |
231
+ | 规则 | 说明 |
232
+ | ---- | ---- |
233
+ | 基础 | `customerName` / `campaign` 非空;`Budget > 0`;地理/语言至少 1 项 |
234
+ | 网络 | `TargetPartnerSearchNetwork` false;不可同时关闭 Google 搜索与搜索网络 |
235
+ | RSA / 关键词 / 附加信息 | 字符上限、词面非空;RSA `Path1`/`Path2` 必填;SITELINK 行长度限制 |
236
+ | 实务 | 日期格式与先后、出价策略与配套字段一致 |
237
237
 
238
238
  `ad campaign-validate` 通过不保证 BatchJob 成功(例如仅写 `DestinationUrl` 未写 `Finalurl` 时 validate 仍可能 ✅)。异步结果用 `ad batch get` 轮询;`HasFailed` / 部分失败时用 `ad batch diff` 对照 JSON 补缺,系列级失败时改 JSON 重提,勿在半成品上反复整包创建。写操作须 `--commit`,见 `references/google-ads/google-ads.md` § ad campaign-create。
@@ -2,7 +2,7 @@
2
2
 
3
3
  `siluzan-tso ad pmax-create` / `ad pmax-validate` **仅**接受 `--config-file` 指向的 JSON 文件。
4
4
 
5
- **与 Search 智投隔离**:勿使用 `campaign-create-template.json` 或 `ad campaign-create`(BatchJob / `campaign-batch-asyncs`)。PMax 走 Google 网关 **`POST /accounts/{accountId}/campaign/pmax`**,同步返回 `campaignId`。
5
+ **与 Search 智投隔离**:勿使用 `campaign-create-template.json` 或 `ad campaign-create`。PMax 使用 `ad pmax-create`,同步返回 `campaignId`。
6
6
 
7
7
  模板 JSON:同目录 [`pmax-create-template.json`](pmax-create-template.json)。
8
8
 
@@ -92,4 +92,4 @@ siluzan-tso ad campaigns -a <accountId> --json-out ./snap
92
92
  - **支持**:Lead Gen / 非 Shopping 的 Performance Max
93
93
  - **不含**:Shopping PMax、Listing groups;信号与其它编辑见 `ad pmax-signals-*` 等(`references/google-ads/pmax-api.md`)
94
94
 
95
- 网关完整契约见 Sammamish:`src/GoogleAPI/docs/pmax-frontend-api.md`;CLI 摘录见 `references/google-ads/pmax-api.md`。
95
+ 命令与流程见 `references/google-ads/pmax-api.md`。
@@ -20,7 +20,7 @@
20
20
  | `accounts/accounts.md` | 列表、余额、消耗、分享、MCC/BC/BM、balance-scan、accounts-digest |
21
21
  | `accounts/currency.md` | CNY/USD 字段来源、符号、跨币种禁止求和 |
22
22
  | `accounts/open-account-by-media.md` | 各媒体开户命令与参数 |
23
- | `accounts/open-account-google-ui.md` | Google 开户网页 CLI 字段对照 |
23
+ | `accounts/open-account-google-ui.md` | Google 开户字段与 Agent 流程 |
24
24
  | `accounts/finance.md` | 转账、开票、充值 |
25
25
  | `accounts/write-audit-restore.md` | 写审计、`--commit`、restore |
26
26
 
@@ -49,11 +49,9 @@ siluzan-tso list-accounts --page 2 --page-size 50
49
49
 
50
50
  ## account-active-bills — 账户激活充值账单明细
51
51
 
52
- 查询指定广告账户在平台上的**激活/充值类账单**(与网页账户管理侧一致)。对应 TSO:
52
+ 查询指定广告账户在平台上的**激活/充值类账单**。
53
53
 
54
- `GET {apiBaseUrl}/AccountActiveBills/{Media}/{entityId}`
55
-
56
- 路径末尾的 **`entityId`** 必须为 **`list-accounts --json-out ./snap`** 返回的账户 **`entityId`**(UUID 形态),**不能**传 `mediaCustomerId` 数字 ID。请求会带 **Datapermission**(与浏览器一致)。
54
+ 路径中的 **`entityId`** 必须为 **`list-accounts --json-out`** 返回的 **`entityId`**(UUID),**不能**传 `mediaCustomerId`。
57
55
 
58
56
  ```bash
59
57
  siluzan-tso account-active-bills -m <媒体> --id <entityId> [--json-out ./snap]
@@ -266,8 +264,6 @@ siluzan-tso account-history --start 2026-03-01 --end 2026-03-31 --json-out ./sna
266
264
 
267
265
  ## account — 账号管理(OAuth 授权 / 解除关联 / Google MCC / 分享)
268
266
 
269
- > 与网页 **`/foreign_trade/tso/manageAccounts`** 对应:`account auth` = 添加授权;`account delink` = 解除授权(从丝路赞解绑账户);`mcc-bind` / `mcc-unbind` = MCC 绑定与解绑。
270
-
271
267
  ### auth — 添加媒体平台 OAuth 授权
272
268
 
273
269
  在浏览器中打开对应媒体的 OAuth 授权页面,授权后账户自动绑定到丝路赞。
@@ -299,7 +295,7 @@ siluzan-tso account auth -m Meta
299
295
 
300
296
  ### delink — 解除授权 / 断开账户关联
301
297
 
302
- 从当前丝路赞账号下移除媒体账户绑定(网页上常称为「解除授权」或「解绑」)。
298
+ 从当前丝路赞账号下移除媒体账户绑定。
303
299
 
304
300
  ```bash
305
301
  siluzan-tso account delink --id <entityId>
@@ -327,7 +323,7 @@ siluzan-tso account delink --ids abc123,def456,ghi789
327
323
 
328
324
  ### mcc-bind — Google MCC 绑定
329
325
 
330
- 将 **子账户**(Google `mediaCustomerId`)挂到指定 **经理账户(MCC)** 下。请求走 **`googleApiUrl`**(与网页一致),需先 `config show` 确认已配置。
326
+ 将 **子账户**(Google `mediaCustomerId`)挂到指定 **经理账户(MCC)** 下。请求走 **`googleApiUrl`**,需先 `config show` 确认已配置。
331
327
 
332
328
  ```bash
333
329
  siluzan-tso account mcc-bind --customers <mediaCustomerId> --mcc <MCC客户ID>
@@ -337,7 +333,7 @@ siluzan-tso account mcc-bind --customers 111,222 --mcc "333;444"
337
333
  | 选项 | 说明 |
338
334
  | ------------------- | -------------------------------------------------------------------------------------- |
339
335
  | `--customers <ids>` | 子账户 `mediaCustomerId`,多个逗号分隔(来自 `list-accounts` 的 `ma.mediaCustomerId`) |
340
- | `--mcc <ids>` | MCC 的客户 ID;多个可用英文逗号、中文逗号、分号、顿号等分隔(与网页输入规则一致) |
336
+ | `--mcc <ids>` | MCC 的客户 ID;多个可用英文逗号、中文逗号、分号、顿号等分隔 |
341
337
  | `--json-out` | 输出每个子账户接口的原始返回,便于排查 |
342
338
 
343
339
  ---
@@ -405,14 +401,14 @@ siluzan-tso account share-detail --customer-id 1234567890
405
401
 
406
402
  ## open-account — 开户申请
407
403
 
408
- > **Google 与网页 `/openAnAccount` 的对照**(五步进度、两步表单、币种/时区下拉):见 **`references/accounts/open-account-google-ui.md`**。
409
- > **各媒体路由、资料、提交接口都不相同**(不只 Google 用 `/openAnAccount`):见 **`references/accounts/open-account-by-media.md`**。
404
+ > Google 开户字段与向导说明见 **`references/accounts/open-account-google-ui.md`**。
405
+ > 各媒体开户参数与接口差异见 **`references/accounts/open-account-by-media.md`**。
410
406
 
411
407
  ### 广告主组 magKey 说明
412
408
 
413
409
  **所有媒体(Google / TikTok / Yandex / BingV2 / Kwai)开户均无需手动查询或填写 magKey。**
414
410
 
415
- CLI 与网页行为完全一致:提交时按 `--company`(或 `--advertiser-name` / `--company-name`)在后台自动查找同名广告主组——存在则更新,不存在则创建——再用拿到的 magKey 提交开户。
411
+ 提交时按 `--company`(或 `--advertiser-name` / `--company-name`)自动查找同名广告主组——存在则更新,不存在则创建——再用 magKey 提交开户。
416
412
 
417
413
  `--advertiser-id` 在所有媒体开户命令中均为**可选**,仅供调试或特殊场景手动指定。
418
414
 
@@ -422,13 +418,11 @@ CLI 与网页行为完全一致:提交时按 `--company`(或 `--advertiser-n
422
418
 
423
419
  ### Google 开户(无需图片)
424
420
 
425
- **更贴近网页交互:**
426
-
427
421
  ```bash
428
- # 交互向导(说明「五步」进度条 + 按网页第 1、2 步顺序提问;需真实终端 TTY)
422
+ # 交互向导(需真实终端 TTY;Agent 环境不可用)
429
423
  siluzan-tso open-account google-wizard
430
424
 
431
- # 时区列表(与网页第二步「时区」下拉同一接口,可加 --keyword 过滤)
425
+ # 时区列表(可加 --keyword 过滤)
432
426
  siluzan-tso open-account google-timezones
433
427
  siluzan-tso open-account google-timezones --keyword Hong
434
428
  ```
@@ -447,7 +441,7 @@ siluzan-tso open-account google \
447
441
  ```
448
442
 
449
443
  > 推广链接可只写域名(如 `www.brand-a.com`),CLI 会自动补 `https://`。
450
- > 可选:`--industry1` / `--industry2`(网页端行业已弱化,多数情况可不填)。
444
+ > 可选:`--industry1` / `--industry2`(多数情况可不填)。
451
445
  > 可选:`--advertiser-id <magKey>` 仅用于调试或必须指定已有组时。
452
446
 
453
447
  | 选项 | 说明 | 必填 |
@@ -499,7 +493,7 @@ siluzan-tso open-account tiktok \
499
493
  ```
500
494
 
501
495
  > CLI 自动完成:① 上传图片到 TikTok 取得 `license_image_id`;② 存档到丝路赞;③ 按公司名自动创建/关联广告主组;④ 检查是否需要法人银联验证。
502
- > **注意**:`--company` 和 `--license-no` 需用户手动填写;网页上传执照图片有 OCR 自动识别,CLI 无此功能。
496
+ > **注意**:`--company` 和 `--license-no` 需用户手动提供;CLI **无**执照 OCR
503
497
 
504
498
  **需要法人银联验证时追加:**
505
499
 
@@ -582,9 +576,7 @@ siluzan-tso open-account kwai \
582
576
 
583
577
  > 仅支持 **TikTok** 账户。关闭后账户停止投放,如需恢复请联系丝路赞客服,操作**不可自助撤销**,谨慎使用。
584
578
  >
585
- > 对应网页:`/foreign_trade/tso/manageAccounts` 选中 TikTok 账户 关闭账户
586
- >
587
- > **实现说明(与网页 `manageAccounts` 一致)**:先经 TikTok 侧 `CheckAdvDisable` 校验(不满足条件如余额未清零会失败),再 `POST .../AdvertiserDisable`;Body 为各账户的 **entityId**。你只需传入 `list-accounts` 中的 **mediaCustomerId**,CLI 会解析 entityId;旧版误把 mediaCustomerId 当 Body 会导致 HTTP 成功但关户不生效。
579
+ > 先经 TikTok `CheckAdvDisable` 校验(余额未清零等会失败)。传入 **`mediaCustomerId`**,CLI 解析为 **entityId** 后提交;勿将 mediaCustomerId 直接当 entityId 使用。
588
580
 
589
581
  ```bash
590
582
  siluzan-tso account close --accounts <mediaCustomerId>
@@ -614,8 +606,6 @@ siluzan-tso account close --accounts 1234567890123456,9876543210654321
614
606
  ## account bm-bind — Meta BM 绑定
615
607
 
616
608
  > 将 Meta 广告账户绑定到指定的 **Business Manager(商务管理平台)**。
617
- >
618
- > 对应网页:`/foreign_trade/tso/manageAccounts` → 选中 Meta 账户 → BM 绑定
619
609
 
620
610
  ```bash
621
611
  siluzan-tso account bm-bind --account-id <mediaCustomerId> --bm-id <bmId>
@@ -701,8 +691,6 @@ siluzan-tso account withdraw-submit --accounts id1,id2,id3
701
691
  ## account bc-bind — TikTok BC 绑定
702
692
 
703
693
  > 将 TikTok 广告账户绑定到 **Business Center(BC,商务中心)**。
704
- >
705
- > 对应网页:`/foreign_trade/tso/manageAccounts` → 选中 TikTok 账户 → BC 绑定
706
694
 
707
695
  ```bash
708
696
  siluzan-tso account bc-bind --customers <mediaCustomerId> --bc-ids <bcId>
@@ -758,7 +746,7 @@ siluzan-tso account email-auth-list -c <mediaCustomerId> [--agent-type <type>]
758
746
 
759
747
  | 选项 | 说明 |
760
748
  | ------------------------ | -------------------------------------------------------------------------- |
761
- | `-c, --customer-id <id>` | Google 广告账户 `mediaCustomerId`(与网页查询参数 `customerId` 一致) |
749
+ | `-c, --customer-id <id>` | Google 广告账户 `mediaCustomerId` |
762
750
  | `--agent-type <type>` | 可选;平台需要时再传(与 `list-accounts --json-out ./snap` 的 `ma.accountType` 一致) |
763
751
  | `--json-out` | 输出原始 JSON |
764
752
 
@@ -7,8 +7,6 @@
7
7
 
8
8
  ## invoice-info — 发票抬头管理
9
9
 
10
- 对应页面:`https://www-ci.siluzan.com/v3/foreign_trade/settings/invoiceInformation`
11
-
12
10
  发票抬头是开票申请时使用的公司/企业信息模板,支持三种类型:
13
11
 
14
12
  - **PI**:形式发票(境外美金账户,英文信息)
@@ -98,7 +96,7 @@ siluzan-tso invoice-info delete <id>
98
96
 
99
97
  ```bash
100
98
  siluzan-tso config show
101
- # 输出中 webUrl 行即为前端基地址,例如:
99
+ # 输出中 webUrl 行即为网页基地址,例如:
102
100
  # webUrl : https://www.siluzan.com (生产环境)
103
101
  # webUrl : https://www-ci.siluzan.com (测试环境)
104
102
  ```
@@ -107,7 +105,7 @@ siluzan-tso config show
107
105
 
108
106
  ### 功能总览
109
107
 
110
- | 功能 | 对应页面路径 | CLI 支持 |
108
+ | 功能 | 引导用户打开的网页路径 | CLI 支持 |
111
109
  | ---------------------------- | ------------------------------------ | --------------------------------------- |
112
110
  | 现金充值(单笔) | `/recharge/pay` | ❌ 引导网页 |
113
111
  | 现金充值(批量) | `/recharge/pay_batch` | ❌ 引导网页 |
@@ -269,7 +267,7 @@ siluzan-tso invoice billable -m Google --json-out ./snap
269
267
 
270
268
  ### invoice apply — 提交开票申请
271
269
 
272
- 与 Web「申请开票」一致的业务规则:
270
+ 开票业务规则:
273
271
 
274
272
  - **人民币(CNY)订单**:只能开 **增值税** 发票(`VATI` / `VATSI`),不能选形式发票 `PI`。
275
273
  - **美金等外币订单**(常见为 USD,且不含 CNY):只能开 **形式发票 `PI`**(系统自动出具),不能选增值税票。
@@ -318,7 +316,7 @@ siluzan-tso invoice apply \
318
316
  | `--recipient-phone` | 收件人手机号 | ✅ |
319
317
  | `--recipient-email` | 推送邮箱 | — |
320
318
  | `--company-name-en` | 公司英文名称(PI 必填) | PI✅ |
321
- | `--registered-address-en` | 英文单位地址(PI 必填,与 Web 校验一致) | PI✅ |
319
+ | `--registered-address-en` | 英文单位地址(PI 必填) | PI✅ |
322
320
  | `--company-name` | 公司中文名称(VATI/VATSI 必填) | VATI✅ |
323
321
  | `--tax-id` | 税号(VATI/VATSI 必填) | VATI✅ |
324
322
  | `--title` | 发票抬头(VATI/VATSI 必填) | VATI✅ |
@@ -368,7 +366,7 @@ siluzan-tso invoice apply \
368
366
 
369
367
  - 用户给出自定义抬头(公司名、税号、抬头文案、地址、联系人等)时:
370
368
  1. 先用 `invoice-info list`(可加 `-k` 关键字、`--invoice-type`)在**已有抬头**中检索:公司名、`title`、税号等与用户输入是否**实质相同**(允许轻微空格/标点差异)。
371
- 2. **若已存在等价记录**:不要重复 `invoice-info create`;直接沿用该条里的字段填 `invoice apply`(与 Web 一致:`apply` 传的是完整 `InvoiceInfomation`,不是只传抬头 id)。
369
+ 2. **若已存在等价记录**:不要重复 `invoice-info create`;直接沿用该条字段填 `invoice apply`(传完整 `InvoiceInfomation`,不是只传抬头 id)。
372
370
  3. **若不存在**:先 `siluzan-tso invoice-info create ...` 创建成功,再 `siluzan-tso invoice apply ...`。避免未落库就假定「已保存」。
373
371
 
374
372
  #### 4. 最后调用开票接口
@@ -1,6 +1,5 @@
1
1
  # 各媒体开户
2
2
 
3
- > 对应页面:`/accountOpeningHistory`
4
3
  > 网页链接:`{webUrl}/v3/foreign_trade/tso/accountOpeningHistory?tso=%2Fv3umijs%2Ftso%2FaccountOpeningHistory`
5
4
  > 多命令串联见 `references/core/workflows.md` § 流程一。
6
5
 
@@ -17,7 +16,7 @@
17
16
 
18
17
  **所需信息**:公司名称、推广网址、推广类型(b2b/b2c/app)、账户名称、币种(CNY/USD)、时区、邀请邮箱
19
18
 
20
- **网页对照**(字段映射):`references/accounts/open-account-google-ui.md`
19
+ **Google 字段说明**:`references/accounts/open-account-google-ui.md`
21
20
 
22
21
  **常用时区**(完整:`siluzan-tso open-account google-timezones`):
23
22
 
@@ -117,7 +116,7 @@ siluzan-tso account-history -m BingV2
117
116
 
118
117
  **所需信息**:公司名称、主体信息、行业多级 ID、产品网址、账户类型、营业执照图片路径
119
118
 
120
- > **双上传**(与网页 `KwaiOpenAnAccount.vue` 一致):CLI `POST /command/attachment` 得 `imageId`(更新广告主组 MAG),再 `POST /KwaiAccount/Management/Upload` `blobstoreKey`(写入 `KwaiAccountInfo.mainCertPhotos`)。二者不可混用。
119
+ > **双上传**:CLI 先上传附件得 `imageId`(更新广告主组 MAG),再上传 Kwai 资质得 `blobstoreKey`(写入 `mainCertPhotos`)。二者不可混用。
121
120
 
122
121
  ```bash
123
122
  siluzan-tso open-account kwai \
@@ -1,64 +1,42 @@
1
- ## 一、页面顶部「五步」是什么?
1
+ ## 一、开户流程(五步语义)
2
2
 
3
- 与**表单分步**不是同一套数字,但语义连贯:
3
+ | 步骤 | 含义 | CLI / Skill |
4
+ | ---- | ---- | ----------- |
5
+ | ① | 准备资料 | 向用户说明清单;无法代替用户准备资质 |
6
+ | ② | 填写并提交 | `open-account google-wizard`(TTY)或 `open-account google` |
7
+ | ③ | 等待审核 | `account-history -m Google` |
8
+ | ④ | 审核通过 | 同上 |
9
+ | ⑤ | 充值激活 | **必须网页**;`config show` → `webUrl` + 充值路径(见 `finance.md`) |
4
10
 
5
- | 步骤 | 文案(意译) | 用户要做什么 | CLI / Skill 能做什么 |
6
- | ---- | ------------ | ---------------------------------- | ------------------------------------------------------------- |
7
- | ① | 准备开户资料 | 准备公司名、网址、类型、账户信息等 | 说明清单;**无法**代替用户准备资质 |
8
- | ② | 填写申请 | 网页里完成第 1、2 步表单并提交 | **`open-account google-wizard`** 或 **`open-account google`** |
9
- | ③ | 等待审核 | 等待媒体/平台审核 | **`account-history -m Google`** 轮询 |
10
- | ④ | 审核通过 | 查看通过结果 | 同上 |
11
- | ⑤ | 充值并激活 | **必须在网页完成充值** | 引导 `config show` → `webUrl` + 充值路径(见 `accounts/finance.md`) |
12
-
13
- 页面上还有提示:**美元账户最低充值约 100 USD、人民币约 700 CNY**(以实际页面为准)。
11
+ 美元账户最低充值约 **100 USD**,人民币约 **700 CNY**(以平台为准)。
14
12
 
15
13
  ---
16
14
 
17
- ## 二、网页里实际填表是「两步」
18
-
19
- ### 第 1 步(企业 / 推广信息)
20
-
21
- | 网页控件 | 说明 | CLI 对应 |
22
- | -------- | -------------------------------------------- | ---------------------------------------------- |
23
- | 公司名称 | 文本,可联想已有广告主 | `--company` |
24
- | 网址 | 左侧下拉 `https://` / `http://` + 右侧输入框 | `--promotion-link`(可只写域名,CLI 会补协议) |
25
- | 类型 | 单选 **B2B / B2C / APP** | `--promotion-type`:`b2b` \| `b2c` \| `app` |
26
- | 行业级联 | 当前前端已隐藏 | 可选 `--industry1` / `--industry2` |
27
-
28
- 提交第 1 步后,后台会**创建或更新广告主组**并拿到 `magKey`;CLI 已内置该逻辑,**无需**用户自己查 `magKey`。
29
-
30
- ### 第 2 步(广告账户表格,与下拉菜单一致)
31
-
32
- | 网页控件 | 说明 | CLI 对应 |
33
- | -------- | ------------------------------------------------- | -------------------------------------------------------- |
34
- | 账户名称 | 文本,默认与公司名一致 | `--account-name` |
35
- | 币种 | 下拉:**CNY / USD**(HKD 对代理商场景为禁用) | `--currency`:`CNY` \| `USD` |
36
- | 时区 | 下拉:文案为 **`(Time)Name`**,值为 **IANA Code** | `--timezone`;列表用 **`open-account google-timezones`** |
37
- | 开户数量 | 1~3,且多行总和 ≤3(网页表格) | `--counts`(1~3) |
38
- | 开户邮箱 | 接收 Google 账户邀请的邮箱(不限 Gmail / `.com`) | `--invite-email` |
15
+ ## 二、非交互提交字段
39
16
 
40
- **币种 默认时区(与网页 `changeCurrency` 一致)**
17
+ | 字段 | CLI 选项 |
18
+ | ---- | -------- |
19
+ | 公司名称 | `--company` |
20
+ | 推广网址 | `--promotion-link`(可只写域名,CLI 补协议) |
21
+ | 推广类型 B2B/B2C/APP | `--promotion-type`:`b2b` \| `b2c` \| `app` |
22
+ | 行业(可选) | `--industry1` / `--industry2` |
23
+ | 账户名称 | `--account-name` |
24
+ | 币种 CNY/USD | `--currency` |
25
+ | 时区 IANA | `--timezone`;列表:`open-account google-timezones` |
26
+ | 开户数量 1~3 | `--counts` |
27
+ | 邀请邮箱 | `--invite-email` |
41
28
 
42
- - 选 **CNY**默认 **`Asia/Shanghai`**
43
- - 选 **USD**(或其它非 CNY)→ 默认 **`Asia/Hong_Kong`**
29
+ **币种默认时区**:CNY → `Asia/Shanghai`;USD(或其它非 CNY)→ `Asia/Hong_Kong`。其它时区从 `google-timezones` 取 **Code** 列。
44
30
 
45
- 其它时区请从 **`siluzan-tso open-account google-timezones`** 里取 **Code** 列(与网页下拉同一接口)。
31
+ 提交时按 `--company` 自动查找/创建广告主组并拿 `magKey`,**无需**手动填 magKey。
46
32
 
47
33
  ---
48
34
 
49
35
  ## 三、推荐用法
50
36
 
51
- ### 1)非交互提交(Agent / 脚本首选)
37
+ ### 非交互(Agent / 脚本首选)
52
38
 
53
39
  ```bash
54
- # 常用时区速查(无需每次运行 google-timezones):
55
- # Asia/Shanghai 北京/上海(CNY 默认)
56
- # Asia/Hong_Kong 香港(USD 默认)
57
- # America/New_York 美东
58
- # America/Los_Angeles 美西
59
- # Europe/London 伦敦
60
- # 完整列表:siluzan-tso open-account google-timezones [--keyword <关键词>]
61
-
62
40
  siluzan-tso open-account google \
63
41
  --company "某某公司" \
64
42
  --promotion-link "https://www.example.com" \
@@ -70,49 +48,40 @@ siluzan-tso open-account google \
70
48
  --counts 1
71
49
  ```
72
50
 
73
- ### 2)交互向导(人工终端操作,需要真实 TTY)
51
+ 常用时区:`Asia/Shanghai`、`Asia/Hong_Kong`、`America/New_York`、`America/Los_Angeles`、`Europe/London`。完整列表:`open-account google-timezones [--keyword <关键词>]`
52
+
53
+ ### 交互向导(需真实 TTY;Agent 不可用)
74
54
 
75
55
  ```bash
76
- # 注意:需要真实终端,CI / 管道 / AI Agent 环境下无法使用
77
56
  siluzan-tso open-account google-wizard
78
57
  ```
79
58
 
80
- ### 3)审核与后续
59
+ ### 审核与充值
81
60
 
82
61
  ```bash
83
- # 轮询审核进度
84
62
  siluzan-tso account-history -m Google
85
-
86
- # 提交后需在网页查看/确认:开户记录(勿用 manageAccounts)
87
- # {webUrl}/v3/foreign_trade/tso/accountOpeningHistory?tso=%2Fv3umijs%2Ftso%2FaccountOpeningHistory
88
- # (非交互 `open-account google` 成功时 CLI 也会打印该链接)
89
-
90
- # 审核通过后,充值激活(必须网页完成)
91
- # siluzan-tso config show 取 webUrl,打开:{webUrl}/v3/foreign_trade/tso/recharge
92
- # 美元账户最低约 100 USD,人民币账户约 700 CNY
63
+ # 审核通过后:config show → {webUrl}/v3/foreign_trade/tso/recharge
93
64
  ```
94
65
 
95
66
  ---
96
67
 
97
- ## 四、给 AI Agent 的简短指令模板
98
-
99
- 当用户说「我要在丝路赞开 Google 户」时:
68
+ ## 四、Agent 指令模板
100
69
 
101
- 1. 收集必填字段:公司名、推广网址、推广类型、账户名、币种、时区、邀请邮箱。
102
- 2. 时区未知时,先给出常用时区表(见上方速查),或运行 `open-account google-timezones --keyword <关键词>` 辅助选择。
103
- 3. 拼一条 `open-account google` 非交互命令提交。
104
- 4. 提交后:`account-history -m Google` 轮询进度。
105
- 5. 审核通过后:引导充值激活,给出充值网页路径(见第三步)。
70
+ 1. 收集:公司名、网址、推广类型、账户名、币种、时区、邀请邮箱。
71
+ 2. 时区不明时运行 `google-timezones` 或给出常用时区表。
72
+ 3. 执行 `open-account google`。
73
+ 4. `account-history -m Google` 轮询。
74
+ 5. 通过后引导充值(`finance.md`)。
106
75
 
107
76
  ---
108
77
 
109
- ## 五、相关命令速查
78
+ ## 五、命令速查
110
79
 
111
- | 命令 | 作用 |
112
- | ------------------------------- | --------------------------------------- |
113
- | `open-account google-wizard` | 交互向导(对齐网页两步表单 + 五步说明) |
114
- | `open-account google-timezones` | 时区列表(网页下拉同源) |
115
- | `open-account google` | 非交互一次性提交 |
116
- | `account-history -m Google` | 开户审核进度 |
80
+ | 命令 | 作用 |
81
+ | ---- | ---- |
82
+ | `open-account google-wizard` | 交互向导 |
83
+ | `open-account google-timezones` | 时区列表 |
84
+ | `open-account google` | 非交互提交 |
85
+ | `account-history -m Google` | 审核进度 |
117
86
 
118
- 更完整的参数表见 `accounts/accounts.md` → **open-account** 章节。
87
+ 完整参数见 `accounts/accounts.md` → **open-account**。