siluzan-tso-cli 1.1.28-beta.2 → 1.1.28-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 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.28-beta.2),供内部测试使用。正式发布后安装命令将改为 `npm install -g siluzan-tso-cli`。
54
+ > **注意**:当前为测试版(1.1.28-beta.4),供内部测试使用。正式发布后安装命令将改为 `npm install -g siluzan-tso-cli`。
55
55
 
56
56
  | 助手 | 建议 `--ai` |
57
57
  | ----------------------- | ------------------------------------ |
package/dist/index.js CHANGED
@@ -106072,6 +106072,55 @@ async function writeReportAnalysisSnapshot(params) {
106072
106072
 
106073
106073
  // src/commands/report/main.ts
106074
106074
  init_version();
106075
+
106076
+ // src/commands/facebook-analysis/fetch.ts
106077
+ init_auth();
106078
+ function normalizeFacebookAccountId(raw) {
106079
+ const t = raw.trim();
106080
+ if (!t) {
106081
+ console.error("\n\u274C --account \u4E0D\u80FD\u4E3A\u7A7A\u3002\n");
106082
+ process.exit(1);
106083
+ }
106084
+ const digits = t.startsWith("act_") ? t.slice(4) : t;
106085
+ if (!/^\d+$/.test(digits)) {
106086
+ console.error(
106087
+ "\n\u274C --account \u987B\u4E3A Facebook \u5E7F\u544A\u8D26\u6237 ID\uFF08\u6570\u5B57 mediaCustomerId\uFF0C\u6216 act_<\u6570\u5B57>\uFF0C\u4E0E list-accounts -m MetaAd \u4E00\u81F4\uFF09\u3002\n"
106088
+ );
106089
+ process.exit(1);
106090
+ }
106091
+ return { apiId: `act_${digits}`, manifestId: digits };
106092
+ }
106093
+ function resolveFacebookDateRange(start, end) {
106094
+ if (start && end) return { startDate: start, endDate: end };
106095
+ if (!start && !end) {
106096
+ const endD = /* @__PURE__ */ new Date();
106097
+ endD.setDate(endD.getDate() - 1);
106098
+ const startD = new Date(endD);
106099
+ startD.setDate(startD.getDate() - 6);
106100
+ const fmt2 = (d) => d.toISOString().slice(0, 10);
106101
+ return { startDate: fmt2(startD), endDate: fmt2(endD) };
106102
+ }
106103
+ console.error("\n\u274C --start \u4E0E --end \u987B\u540C\u65F6\u4F20\u5165\uFF0C\u6216\u540C\u65F6\u7701\u7565\u4EE5\u4F7F\u7528\u9ED8\u8BA4\u8FD1 7 \u5929\uFF08\u622A\u81F3\u6628\u5929\uFF09\u3002\n");
106104
+ process.exit(1);
106105
+ }
106106
+ function facebookReportingUrl(config, apiId, segment, query) {
106107
+ const q = query ? query.startsWith("?") ? query : `?${query}` : "";
106108
+ return `${config.apiBaseUrl}/reporting/media-account/FacebookAds/${encodeURIComponent(apiId)}/${segment}${q}`;
106109
+ }
106110
+ async function fetchFacebookSectionPayload(def, opts, config, apiId) {
106111
+ const { startDate, endDate } = resolveFacebookDateRange(opts.start, opts.end);
106112
+ const params = new URLSearchParams({ startDate, endDate });
106113
+ if (def.countryLimitOption && opts.limit !== void 0 && Number.isFinite(opts.limit)) {
106114
+ params.set("limit", String(Math.max(1, Math.floor(opts.limit))));
106115
+ }
106116
+ const url = facebookReportingUrl(config, apiId, def.segment, params.toString());
106117
+ return apiFetch2(url, config, {}, opts.verbose ?? false);
106118
+ }
106119
+ function endpointHintForFacebookSection(def, apiId) {
106120
+ return `GET \u2026/FacebookAds/${apiId}/${def.segment}`;
106121
+ }
106122
+
106123
+ // src/commands/report/main.ts
106075
106124
  async function runReportList(opts) {
106076
106125
  if (!VALID_MEDIA_TYPES5.includes(opts.media)) {
106077
106126
  console.error(`
@@ -106173,30 +106222,61 @@ function fmtNum2(n, digits = 2) {
106173
106222
  parts[0] = parts[0].replace(/\B(?=(\d{3})+(?!\d))/g, ",");
106174
106223
  return parts.join(".");
106175
106224
  }
106225
+ function metaOverviewManifestId(raw) {
106226
+ const t = raw.trim();
106227
+ if (t.startsWith("act_")) return t.slice(4);
106228
+ return t;
106229
+ }
106230
+ function buildMetaOverviewUrl(config, accountId, mediaAccountType, startDate, endDate) {
106231
+ const { apiId, manifestId } = normalizeFacebookAccountId(accountId);
106232
+ const params = new URLSearchParams({ startDate, endDate }).toString();
106233
+ if (mediaAccountType === "FacebookAds") {
106234
+ return facebookReportingUrl(config, apiId, "OverviewSectionData", params);
106235
+ }
106236
+ return `${config.apiBaseUrl}/reporting/media-account/MetaAd/${manifestId}/OverviewSectionData?${params}`;
106237
+ }
106176
106238
  async function runReportMetaOverview(opts) {
106177
- const id = opts.account.trim();
106178
- if (!/^\d+$/.test(id)) {
106179
- console.error("\n\u274C --account \u987B\u4E3A\u6570\u5B57 mediaCustomerId\uFF08\u4E0E list-accounts \u4E00\u81F4\uFF09\u3002\n");
106239
+ const rawAccount = opts.account.trim();
106240
+ let manifestId;
106241
+ try {
106242
+ manifestId = normalizeFacebookAccountId(rawAccount).manifestId;
106243
+ } catch {
106244
+ console.error(
106245
+ "\n\u274C --account \u987B\u4E3A\u6570\u5B57 mediaCustomerId \u6216 act_<\u6570\u5B57>\uFF08\u4E0E list-accounts -m MetaAd \u4E00\u81F4\uFF09\u3002\n"
106246
+ );
106180
106247
  process.exit(1);
106181
106248
  }
106182
106249
  const { startDate, endDate } = resolveMetaOverviewDateRange(opts.startDate, opts.endDate);
106183
106250
  const config = loadConfig(opts.token);
106184
- const params = new URLSearchParams({ startDate, endDate });
106185
- const url = `${config.apiBaseUrl}/reporting/media-account/MetaAd/${id}/OverviewSectionData?${params}`;
106251
+ const accountDetail = await lookupMetaAccountForOverview(config, manifestId, opts.verbose);
106252
+ const mediaAccountType = accountDetail?.mediaAccountType ?? "FacebookAds";
106253
+ const url = buildMetaOverviewUrl(config, rawAccount, mediaAccountType, startDate, endDate);
106186
106254
  let data;
106187
106255
  try {
106188
106256
  data = await apiFetch2(url, config, {}, opts.verbose);
106189
106257
  } catch (err) {
106190
- console.error(`
106191
- \u274C \u8BF7\u6C42\u5931\u8D25\uFF1A${err instanceof Error ? err.message : String(err)}
106258
+ const msg = err instanceof Error ? err.message : String(err);
106259
+ if (msg.includes("403")) {
106260
+ console.error(
106261
+ `
106262
+ \u274C \u8BF7\u6C42\u5931\u8D25\uFF1AHTTP 403
106263
+ \u8D26\u6237 ${manifestId} \u7684 mediaAccountType=${mediaAccountType ?? "\u672A\u77E5"}\u3002
106264
+ Facebook \u6388\u6743\u8D26\u6237\uFF08FacebookAds\uFF09\u987B\u8D70 FacebookAds/act_ \u8DEF\u5F84\uFF1B\u4E1D\u8DEF\u8D5E Meta \u5F00\u6237\u8D70 MetaAd \u8DEF\u5F84\u3002
106265
+ \u5468\u671F/\u591A\u7EF4\u5EA6\u62A5\u544A\u8BF7\u6539\u7528\uFF1Asiluzan-tso facebook-analysis -a ${manifestId} --sections overview --json-out <dir>
106266
+ `
106267
+ );
106268
+ } else {
106269
+ console.error(`
106270
+ \u274C \u8BF7\u6C42\u5931\u8D25\uFF1A${msg}
106192
106271
  `);
106272
+ }
106193
106273
  process.exit(1);
106194
106274
  }
106195
106275
  if (opts.jsonOut) {
106196
106276
  const summary = await writeReportAnalysisSnapshot({
106197
106277
  snapshotDir: opts.jsonOut,
106198
106278
  section: "meta-overview",
106199
- accountId: id,
106279
+ accountId: manifestId,
106200
106280
  dateRange: { start: startDate, end: endDate },
106201
106281
  payload: data,
106202
106282
  cliVersion: getCurrentVersion2()
@@ -106211,7 +106291,7 @@ async function runReportMetaOverview(opts) {
106211
106291
  const optRaw = row.optimizationScore;
106212
106292
  const optPct = typeof optRaw === "number" && optRaw <= 1 && optRaw >= 0 ? (optRaw * 100).toFixed(0) : typeof optRaw === "number" ? String(optRaw) : "\u2014";
106213
106293
  console.log(`
106214
- \u2705 Meta \u8D26\u6237\u603B\u89C8 ${id} \u533A\u95F4 ${startDate} ~ ${endDate}
106294
+ \u2705 Meta \u8D26\u6237\u603B\u89C8 ${manifestId} \u533A\u95F4 ${startDate} ~ ${endDate}
106215
106295
  `);
106216
106296
  console.log(
106217
106297
  ` \u8D27\u5E01 ${currency} \u4F18\u5316\u5F97\u5206 ${optPct}${typeof optRaw === "number" && optRaw <= 1 ? "%\uFF080\u20131 \u5DF2\u6362\u7B97\uFF09" : ""} \u603B\u6D88\u8017 ${fmtNum2(typeof row.totalCost === "number" ? row.totalCost : cur.spend)} \u4F59\u989D ${fmtNum2(typeof row.remainingAccountBudget === "number" ? row.remainingAccountBudget : void 0)} \u6D3B\u8DC3\u5929\u6570 ${row.activeDays ?? "\u2014"} \u65E5\u5747\u6D88\u8017 ${fmtNum2(typeof row.averageDailyCost === "number" ? row.averageDailyCost : void 0)}
@@ -106227,6 +106307,42 @@ async function runReportMetaOverview(opts) {
106227
106307
  function toSlashDate(date) {
106228
106308
  return date.replace(/-/g, "/");
106229
106309
  }
106310
+ async function lookupMetaAccountForOverview(config, manifestId, verbose = false) {
106311
+ for (const idCandidate of [manifestId, `act_${manifestId}`]) {
106312
+ const params = new URLSearchParams({
106313
+ MediaTypes: "MetaAd|FacebookAds",
106314
+ advStatus: "",
106315
+ mediaAccountState: "Approved,Linked",
106316
+ isForce: "false",
106317
+ pageNum: "1",
106318
+ pageSize: "20",
106319
+ mediaCustomerIds: idCandidate
106320
+ });
106321
+ try {
106322
+ const data = await apiFetch2(
106323
+ `${config.apiBaseUrl}/query/media-account/SearchMediaAcountByCriteria?${params}`,
106324
+ config,
106325
+ {},
106326
+ verbose
106327
+ );
106328
+ for (const item of data?.mas ?? []) {
106329
+ const ma = item.ma ?? {};
106330
+ const cidDigits = metaOverviewManifestId(String(ma.mediaCustomerId ?? ""));
106331
+ if (cidDigits === manifestId) {
106332
+ return {
106333
+ entityId: String(ma.entityId ?? ""),
106334
+ mediaCustomerId: String(ma.mediaCustomerId ?? ""),
106335
+ mediaCustomerName: String(ma.mediaCustomerName ?? ""),
106336
+ mediaAccountType: String(ma.mediaAccountType ?? ""),
106337
+ mediaAccountGroupId: String(ma.mediaAccountGroupId ?? "")
106338
+ };
106339
+ }
106340
+ }
106341
+ } catch {
106342
+ }
106343
+ }
106344
+ return void 0;
106345
+ }
106230
106346
  async function lookupAccountsByCustomerIds(apiBaseUrl, config, mediaType, customerIds, verbose = false) {
106231
106347
  const params = new URLSearchParams({
106232
106348
  MediaType: mediaType,
@@ -106239,14 +106355,17 @@ async function lookupAccountsByCustomerIds(apiBaseUrl, config, mediaType, custom
106239
106355
  {},
106240
106356
  verbose
106241
106357
  );
106242
- const idSet = new Set(customerIds.map((id) => id.trim()));
106358
+ const idSet = new Set(
106359
+ customerIds.map((id) => metaOverviewManifestId(id.trim())).filter(Boolean)
106360
+ );
106243
106361
  const result = /* @__PURE__ */ new Map();
106244
106362
  const items = Array.isArray(data) ? data : [];
106245
106363
  for (const item of items) {
106246
106364
  const ma = item.ma ?? {};
106247
106365
  const cid = String(ma.mediaCustomerId ?? "");
106248
- if (idSet.has(cid)) {
106249
- result.set(cid, {
106366
+ const cidDigits = metaOverviewManifestId(cid);
106367
+ if (idSet.has(cidDigits)) {
106368
+ result.set(cidDigits, {
106250
106369
  entityId: String(ma.entityId ?? ""),
106251
106370
  mediaCustomerId: cid,
106252
106371
  mediaCustomerName: String(ma.mediaCustomerName ?? ""),
@@ -107480,7 +107599,9 @@ function register13(program2) {
107480
107599
  verbose: opts.verbose
107481
107600
  });
107482
107601
  });
107483
- reportCmd.command("meta-overview").description("Meta \u8D26\u6237\u5206\u6790\u603B\u89C8\uFF08OverviewSectionData\uFF0C\u9700 tsoApiBaseUrl \u9274\u6743\uFF09").requiredOption("-a, --account <id>", "Meta \u5E7F\u544A\u8D26\u6237 mediaCustomerId").option("--start <date>", "\u5F00\u59CB\u65E5\u671F YYYY-MM-DD\uFF08\u4E0E --end \u540C\u4F20\u6216\u540C\u7701\u7565\uFF09").option("--end <date>", "\u7ED3\u675F\u65E5\u671F YYYY-MM-DD").option("--token <token>", "Auth Token").option(
107602
+ reportCmd.command("meta-overview").description(
107603
+ "Meta \u8D26\u6237\u5206\u6790\u603B\u89C8\uFF08OverviewSectionData\uFF1BFacebookAds \u6388\u6743\u6237\u8D70 FacebookAds/act_\uFF0CMetaAd \u5F00\u6237\u8D70 MetaAd\uFF09"
107604
+ ).requiredOption("-a, --account <id>", "Meta \u5E7F\u544A\u8D26\u6237 mediaCustomerId\uFF08\u6570\u5B57\u6216 act_<\u6570\u5B57>\uFF09").option("--start <date>", "\u5F00\u59CB\u65E5\u671F YYYY-MM-DD\uFF08\u4E0E --end \u540C\u4F20\u6216\u540C\u7701\u7565\uFF09").option("--end <date>", "\u7ED3\u675F\u65E5\u671F YYYY-MM-DD").option("--token <token>", "Auth Token").option(
107484
107605
  "--json-out <path>",
107485
107606
  "\u843D\u76D8\u76EE\u5F55\u5E76\u66F4\u65B0 report-manifest[-<accountId>].json\uFF1B\u6587\u4EF6\u540D\u4E3A `<section>[-<accountId>].json`\uFF1Bstdout \u4E00\u884C\u6458\u8981 JSON\uFF0C\u542B outlineFile\uFF08TS \u5F0F\u7C7B\u578B\u5728 `*.outline.txt`\uFF09"
107486
107607
  ).option("--verbose", "\u8BE6\u7EC6\u9519\u8BEF\u4FE1\u606F", false).action(async (opts) => {
@@ -122431,53 +122552,6 @@ var FACEBOOK_SECTION_ALIASES = {
122431
122552
  videos: "material"
122432
122553
  };
122433
122554
 
122434
- // src/commands/facebook-analysis/fetch.ts
122435
- init_auth();
122436
- function normalizeFacebookAccountId(raw) {
122437
- const t = raw.trim();
122438
- if (!t) {
122439
- console.error("\n\u274C --account \u4E0D\u80FD\u4E3A\u7A7A\u3002\n");
122440
- process.exit(1);
122441
- }
122442
- const digits = t.startsWith("act_") ? t.slice(4) : t;
122443
- if (!/^\d+$/.test(digits)) {
122444
- console.error(
122445
- "\n\u274C --account \u987B\u4E3A Facebook \u5E7F\u544A\u8D26\u6237 ID\uFF08\u6570\u5B57 mediaCustomerId\uFF0C\u6216 act_<\u6570\u5B57>\uFF0C\u4E0E list-accounts -m MetaAd \u4E00\u81F4\uFF09\u3002\n"
122446
- );
122447
- process.exit(1);
122448
- }
122449
- return { apiId: `act_${digits}`, manifestId: digits };
122450
- }
122451
- function resolveFacebookDateRange(start, end) {
122452
- if (start && end) return { startDate: start, endDate: end };
122453
- if (!start && !end) {
122454
- const endD = /* @__PURE__ */ new Date();
122455
- endD.setDate(endD.getDate() - 1);
122456
- const startD = new Date(endD);
122457
- startD.setDate(startD.getDate() - 6);
122458
- const fmt2 = (d) => d.toISOString().slice(0, 10);
122459
- return { startDate: fmt2(startD), endDate: fmt2(endD) };
122460
- }
122461
- console.error("\n\u274C --start \u4E0E --end \u987B\u540C\u65F6\u4F20\u5165\uFF0C\u6216\u540C\u65F6\u7701\u7565\u4EE5\u4F7F\u7528\u9ED8\u8BA4\u8FD1 7 \u5929\uFF08\u622A\u81F3\u6628\u5929\uFF09\u3002\n");
122462
- process.exit(1);
122463
- }
122464
- function facebookReportingUrl(config, apiId, segment, query) {
122465
- const q = query ? query.startsWith("?") ? query : `?${query}` : "";
122466
- return `${config.apiBaseUrl}/reporting/media-account/FacebookAds/${encodeURIComponent(apiId)}/${segment}${q}`;
122467
- }
122468
- async function fetchFacebookSectionPayload(def, opts, config, apiId) {
122469
- const { startDate, endDate } = resolveFacebookDateRange(opts.start, opts.end);
122470
- const params = new URLSearchParams({ startDate, endDate });
122471
- if (def.countryLimitOption && opts.limit !== void 0 && Number.isFinite(opts.limit)) {
122472
- params.set("limit", String(Math.max(1, Math.floor(opts.limit))));
122473
- }
122474
- const url = facebookReportingUrl(config, apiId, def.segment, params.toString());
122475
- return apiFetch2(url, config, {}, opts.verbose ?? false);
122476
- }
122477
- function endpointHintForFacebookSection(def, apiId) {
122478
- return `GET \u2026/FacebookAds/${apiId}/${def.segment}`;
122479
- }
122480
-
122481
122555
  // src/commands/facebook-analysis/register-cli.ts
122482
122556
  import { Command } from "commander";
122483
122557
 
@@ -1,8 +1,8 @@
1
1
  ---
2
2
  name: siluzan-tso
3
- description: Operates Siluzan TSO ad accounts via siluzan-tso-cli (Google/Bing/Yandex/TikTok/Kwai/MetaAd). Tasks (accounts) list, balance, stats, spend, active-bills, history, balance-scan P2, accounts-digest P3, share/unshare, delink, OAuth, MCC/BC/BM bind-unbind, email invite, TikTok close, suspend withdraw; (open-account) apply on 6 media; (Google Ads) campaign/adgroup/ad/keyword CRUD, extensions, geo, PMax, search campaign-plan validate/create, batch async create; (analytics) google-analysis, google-analysis-batch P5, facebook-analysis P4-FB, website-diagnosis P8, market-analysis P9, ads diagnosis; (reports) P1 profile, P4 period, P6 OKKI weekly, P7 inquiry; (tools) keyword suggest, RAG query, TSO report list/create/push, AI optimize records, forewarning rules, TikTok/Meta leads, transfer/invoice/invoice-info, audit restore. Use when user mentions ad accounts, balance, spend, campaigns, keywords, open account, MCC/BC, reports, diagnosis, market analysis, invoicing, or TSO. Re-read references/core/agent-conventions.md each new task.
3
+ description: 通过 siluzan-tso-cli 操作丝路赞 TSO 广告账户(Google/Bing/Yandex/TikTok/Kwai/MetaAd)。(账户)列表、余额、统计、消耗、激活账单、历史、余额扫描 P2、账户摘要 P3、共享/取消共享、解绑、OAuthMCC/BC/BM 绑定解绑、邮件邀请、TikTok 关闭、暂停撤回;(开户)六大媒体申请;(Google Ads)系列/广告组/广告/关键词 CRUD、附加信息、地理位置、PMax、搜索投放方案校验/创建、批量异步创建;(分析)google-analysisgoogle-analysis-batch P5facebook-analysis P4-FB、网站诊断 P8、市场分析 P9、广告诊断;P1 账户画像、P4 周期报告、P6 OKKI 周报、P7 询盘分析;Google关键词推荐、RAG 检索、TSO 报告列表/创建/推送、智能预警、TikTok/Meta 线索、转账/发票/发票资料、审计恢复。当用户提及广告账户、余额、消耗、投放、关键词、开户、MCC/BC、报告、诊断、市场分析、开票或 TSO 时使用。
4
4
  license: MIT
5
- compatibility: Requires Node.js 18+, siluzan-tso-cli installed, authenticated via send-login-code + login or config set
5
+ compatibility: 需要 Node.js 18+、已安装 siluzan-tso-cli,通过 send-login-code + login config set 完成鉴权
6
6
  metadata:
7
7
  requires: nodejs,siluzan-tso-cli
8
8
  allowed-tools: Bash(siluzan-tso:*) Read Write
@@ -44,13 +44,14 @@ Windows:部分 Agent 通过 PowerShell 代执行时可能失败,改在 [Git
44
44
 
45
45
  ---
46
46
 
47
- ## 可执行的操作范围
48
47
 
49
- | 级别 | 示例 |
50
- | ---- | ---- |
51
- | **只读** | 账户列表、余额、消耗、报告、预警记录、Google Ads 查询 |
52
- | **写入**(需用户确认) | 创建/修改广告、开户申请、预警规则、报告推送 |
53
- | **破坏性**(确认 + `--commit`) | 账户解绑/关闭、MCC/BC 解绑、删除预警/报告/广告、发票申请 |
48
+ ## 基本使用
49
+
50
+ siluzan-tso-cli只是报名, 安装后可执行的命令是 siluzan-tso
51
+
52
+ ```bash
53
+ siluzan-tso -h // 查看帮助
54
+ ```
54
55
 
55
56
  ---
56
57
 
@@ -1,5 +1,5 @@
1
1
  {
2
2
  "slug": "siluzan-tso",
3
- "version": "1.1.28-beta.2",
4
- "publishedAt": 1780907501825
3
+ "version": "1.1.28-beta.4",
4
+ "publishedAt": 1780970734894
5
5
  }
@@ -265,7 +265,7 @@ const data = d.record ?? d.items; // record 非空=汇总维度;否则=列表
265
265
 
266
266
  > 周期/诊断报告:**`facebook-analysis`**(`FacebookAds` 路径,7 Section,对齐 Google 周期报告能 cover 的部分)。
267
267
  > 撰写与 Google 对照表:**`references/analytics/facebook-analysis-guide.md`**(必读)。
268
- > `report meta-overview` 仅为遗留单维总览(`MetaAd` 路径)。
268
+ > `report meta-overview` 仅为遗留单维总览:按 `list-accounts` 的 `mediaAccountType` 自动选路径——**FacebookAds**(OAuth 授权)→ `FacebookAds/act_<id>`;**MetaAd**(丝路赞开户)→ `MetaAd/<id>`。多维度请用 `facebook-analysis`。
269
269
 
270
270
  ### 周期报告(默认 6 维,等同 Google 周期报告主流程)
271
271
 
@@ -1,5 +1,3 @@
1
- <!-- 本文件由 `pnpm --filter siluzan-tso-cli run gen:cli-enums` 自动生成,勿手改。源码:src/cli-enums/registry.ts -->
2
-
3
1
  # CLI 枚举速查
4
2
 
5
3
  > **Agent**:不确定 `--sections`、`-m`、`--operator` 等合法值时,**Read 本文件**(按章节)或执行:
@@ -159,7 +159,7 @@ Sheet 5 整段系列明细读:`./snap-inquiry/campaigns-<accountId>_<S>-<E>.js
159
159
 
160
160
  ## 国家 → 大洲映射
161
161
 
162
- - **数据源**:`references/analytics/geo-continents.json`(中文国家名 → 7 大洲,覆盖 200+ 主权国家 + 港澳台/巴勒斯坦/马恩岛/波多黎各等领土,由 `tso-cli/scripts/generate-geo-dictionaries.mjs` 与 `geo-en2zh.json` 同步生成;新增国家请加 `ISO_TO_CONTINENT` 映射后跑 `pnpm --filter siluzan-tso-cli run geo:generate`,**不要**手改 JSON)。
162
+ - **数据源**:`references/analytics/geo-continents.json`
163
163
  - **默认 7 桶**:`亚洲` / `中东` / `欧洲` / `北美` / `南美` / `非洲` / `大洋洲`。
164
164
  - **运营常用 5 桶合并**(与样表 Sheet 3 一致):`<重点国>` / `中东` / `非洲` / `欧美` / `亚洲(除重点国)`。**由脚本在写表前做二次合并**,伪代码:
165
165
 
@@ -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.28-beta.2'
12
+ $PKG_VERSION = '1.1.28-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.28-beta.2"
12
+ readonly PKG_VERSION="1.1.28-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"
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "siluzan-tso-cli",
3
- "version": "1.1.28-beta.2",
3
+ "version": "1.1.28-beta.4",
4
4
  "description": "Siluzan 广告账户管理 CLI — 查询账户、余额、消耗数据,管理绑定关系与充值。",
5
5
  "keywords": [
6
6
  "ad-account",
@@ -1,44 +0,0 @@
1
- # Siluzan TSO Skill — 人类导读
2
-
3
- Agent 运行时请用 `SKILL.md` + `AGENTS.md`;本文供维护者与产品经理深读。
4
-
5
- ## 设计理念(对齐 gstack)
6
-
7
- 1. **薄入口**:`SKILL.md` 仅路由,<150 行
8
- 2. **域化 references**:`core/`、`accounts/`、`google-ads/`、`analytics/`、`operations/`
9
- 3. **渐进披露**:Playbook、参数表、SOP 按需 Read,不塞进主文件
10
- 4. **模板分离**:`assets/*.json` 契约 vs `report-templates/*.md` 内容纲要
11
-
12
- ## 目录树
13
-
14
- ```
15
- siluzan-ads/
16
- ├── AGENTS.md # Agent 域目录(类似 gstack AGENTS.md)
17
- ├── SKILL.md # 路由入口
18
- ├── docs/skill-guide.md # 本文件
19
- ├── references/
20
- │ ├── core/ # 纪律、安装、playbooks
21
- │ ├── accounts/ # 账户、开户、财务
22
- │ ├── google-ads/ # CLI 命令 + rules/ SOP
23
- │ ├── analytics/ # 拉数、批处理、拓词
24
- │ └── operations/ # 预警、线索、自动化
25
- ├── assets/ # JSON/MD 创建契约
26
- └── report-templates/ # 报告纲要 + HTML 样式
27
- ```
28
-
29
- ## 常见维护任务
30
-
31
- | 任务 | 改哪里 |
32
- | --- | --- |
33
- | 新 CLI 命令 | `src/commands/` + 对应 `references/<域>/` |
34
- | 新 Playbook | `references/core/playbooks.md` + `SKILL.md` 任务表 |
35
- | 环境 URL | `scripts/write-defaults.mjs` + `copy-skill-assets.mjs` |
36
- | 发布 Skill | `npm run build:prod` → `siluzan-tso init --force` |
37
-
38
- ## 与 eval 的关系
39
-
40
- 场景 JSON 中 `judgeReferencePaths` 须使用域路径,例如 `references/accounts/accounts.md`。改文档后运行:
41
-
42
- ```bash
43
- pnpm --filter siluzan-tso-cli run eval:export-cases
44
- ```
@@ -1,196 +0,0 @@
1
- # Agent SKILL 最佳实践(siluzan-tso 维护指南)
2
-
3
- 本文档基于 [Agent Skills 开放规范](https://agentskills.io/specification) 与 [Cursor Skills 文档](https://cursor.com/docs/skills),结合 siluzan-skill monorepo 的 TSO 实践,供维护 `tso-cli/assets/siluzan-ads/` 时对照。
4
-
5
- ---
6
-
7
- ## 1. 协议要点(agentskills.io)
8
-
9
- ### 1.1 目录结构
10
-
11
- ```
12
- siluzan-tso/ # 安装后目录名
13
- ├── AGENTS.md # Agent 域目录(gstack 风格)
14
- ├── SKILL.md.tmpl → SKILL.md # 构建生成
15
- ├── snippets/ # 共享注入块
16
- ├── docs/skill-guide.md # 人类导读
17
- ├── snippets/ # agent-preamble、handoff-p5/p6/p7(构建时注入 SKILL 或随包复制)
18
- ├── references/
19
- │ ├── core/ # 纪律、安装、playbooks、subagent-orchestration
20
- │ ├── accounts/ # 账户、开户、财务
21
- │ ├── google-ads/ # CLI + rules/
22
- │ ├── analytics/ # 拉数、批处理、拓词
23
- │ └── operations/ # 预警、线索、自动化
24
- ├── assets/
25
- └── report-templates/
26
- ```
27
-
28
- 源码位于 `tso-cli/assets/siluzan-ads/`,构建时 `copy-skill-assets.mjs` 替换占位符后写入 `dist/skill/`。
29
-
30
- ### 1.2 SKILL.md frontmatter
31
-
32
- | 字段 | 要求 | siluzan-tso 用法 |
33
- | ---- | ---- | ---------------- |
34
- | `name` | 小写+连字符,≤64 字,**须与安装目录名一致** | `siluzan-tso`(非 `siluzan-ads`) |
35
- | `description` | ≤1024 字,**第三人称**,含 WHAT + WHEN + 触发词 | 列出媒体、开户、Google Ads、报告、预警等 |
36
- | `license` | 可选 | `MIT` |
37
- | `compatibility` | 可选,≤500 字,环境依赖 | Node 18+、`siluzan-tso-cli`、鉴权 |
38
- | `metadata` | 可选键值 | `requires: nodejs,siluzan-tso-cli` |
39
- | `allowed-tools` | 可选(实验性) | `Bash(siluzan-tso:*) Read Write` |
40
- | `disable-model-invocation` | Cursor 扩展 | TSO 需自动激活,**不设** true |
41
-
42
- ### 1.3 渐进式披露(三层加载)
43
-
44
- | 层级 | 内容 | Token 预算建议 |
45
- | ---- | ---- | -------------- |
46
- | L1 元数据 | `name` + `description` | ~100 tokens,启动时索引 |
47
- | L2 指令 | `SKILL.md` 正文 | <500 行,<5000 tokens,激活时加载 |
48
- | L3 资源 | `references/`、`assets/`、`report-templates/` | 任务相关时才 Read |
49
-
50
- **原则**:SKILL.md 只做路由与不可妥协的纪律;参数表、Playbook 步骤、优化 SOP 放 references。
51
-
52
- ### 1.4 文件引用规则
53
-
54
- - 从 SKILL.md **只链接一层**(`references/foo.md`),避免 A→B→C 链式引用。
55
- - 路径用正斜杠,不用 Windows 反斜杠。
56
- - 占位符 `https://www-ci.siluzan.com`、`https://tso-api-ci.siluzan.com`、`npm install -g siluzan-tso-cli@beta` 由构建脚本替换,文档中禁止硬编码环境 URL。
57
-
58
- ---
59
-
60
- ## 2. 内容编写原则
61
-
62
- ### 2.1 简洁(Concise is Key)
63
-
64
- Agent 已具备通用能力。文档只写 **CLI 特有** 内容:字段口径、批量命令、门禁流程、与前端不一致处。
65
-
66
- ❌ 解释「什么是 Google Ads」
67
- ✅ `budgetAmountYuan` 已是元,禁止再 ÷100
68
-
69
- ### 2.2 单一术语
70
-
71
- 全库统一:
72
-
73
- | 概念 | 统一用词 | 禁止混用 |
74
- | ---- | -------- | -------- |
75
- | 广告账户 ID | `mediaCustomerId` | customer id、账户号(无上下文时) |
76
- | 实体 ID | `entityId` | 内部 id |
77
- | 落盘 | `--json-out` | --json-out ./snap(已移除) |
78
- | 媒体 | `Google`、`TikTok`、`MetaAd`… | google、meta |
79
-
80
- ### 2.3 默认 + 逃生口
81
-
82
- 每个决策点给一个默认路径,例外单独一行:
83
-
84
- ```markdown
85
- 全量 Google 批处理:省略 `-a`(CLI 内部拉清单)。
86
- 仅当用户明确给出 ID 子集时才传 `-a id1,id2,...`。
87
- ```
88
-
89
- ### 2.4 反馈环(Feedback Loop)
90
-
91
- 写操作文档须含:**写命令 → 读命令复核 → 失败时查 reference**。
92
-
93
- 破坏性操作统一:**用户确认 → `--commit` → 读命令验证**。
94
-
95
- ### 2.5 禁止时间敏感表述
96
-
97
- 不写「2025 年 8 月前用旧 API」。废弃内容放 `## 已废弃` 折叠或删除。
98
-
99
- ---
100
-
101
- ## 3. siluzan-tso 文档分层
102
-
103
- | 文件 | 职责 |
104
- | ---- | ---- |
105
- | `SKILL.md` | 安装、能力边界、文档路由表、任务→文档速查、Playbook 索引 |
106
- | `references/core/agent-conventions.md` | Agent 纪律:加载时机、硬规范、数据转换、时间/币种/批量 |
107
- | `references/core/playbooks.md` | P1–P7 标准动作(可复制命令块) |
108
- | `references/core/subagent-orchestration.md` | 主 Agent 自主委派决策矩阵(无 `agents/` 目录) |
109
- | `snippets/handoff-p*.md` | Task handoff 模板(随 skill 安装根复制) |
110
- | `references/core/tips.md` | `--json-out` 协议、outline 处理顺序 |
111
- | `references/*.md` | 单功能域:命令、参数、输出示例 |
112
- | `references/google-ads/rules/` | Google 优化/合规 SOP(非 CLI 参数) |
113
- | `assets/*.json` / `*.md` | 创建类 JSON 契约 |
114
- | `report-templates/*.md` | 报告内容纲要(优先于 HTML) |
115
- | `report-templates/*.html` | 视觉样式,**不含业务数据** |
116
- | `README.md` | 给「通过 URL 安装」的大模型说明(不进 dist 路由) |
117
-
118
- ### 3.1 与 CLI 代码同步
119
-
120
- **代码变更 → 必须同步 Skill**(tutorial-10 #2)。新命令注册在 `_register.ts` / `index.ts` 后:
121
-
122
- 1. 更新对应 `references/*.md`
123
- 2. 更新 `SKILL.md` 路由表(若新功能域)
124
- 3. 评估场景:`tso-cli/eval/scenarios/` + `pnpm --filter siluzan-tso-cli run eval:export-cases`
125
-
126
- ### 3.2 灰度开关(tutorial-10 #11)
127
-
128
- | 状态 | CLI 注册 | SKILL 文档 | AI 可见 |
129
- | ---- | -------- | ---------- | ------- |
130
- | 内测 | 是 | 否 | 否 |
131
- | 公开 | 是 | 是 | 是 |
132
-
133
- ---
134
-
135
- ## 4. description 写法模板
136
-
137
- ```yaml
138
- description: >-
139
- Operates Siluzan TSO ad accounts via siluzan-tso-cli: Google/Bing/Yandex/TikTok/Kwai/Meta
140
- account management, Google Ads CRUD, analytics, optimization reports, forewarning, leads,
141
- invoicing, and RAG knowledge lookup. Use when the user mentions ad accounts, balance, spend,
142
- campaigns, keywords, PMax, open account, MCC/BC binding, or TSO reporting.
143
- Each new task requires re-reading routed references per SKILL.md.
144
- ```
145
-
146
- 中文项目可保留中文 description,但仍须:**第三人称 + 能力列表 + 触发场景 + 重读纪律一句**。
147
-
148
- ---
149
-
150
- ## 5. 体积与质量检查清单
151
-
152
- 发布前逐项确认:
153
-
154
- ### Frontmatter
155
- - [ ] `name` === 安装目录名 `siluzan-tso`
156
- - [ ] `description` 含 WHAT、WHEN、关键词
157
- - [ ] `compatibility` 列出 Node + CLI + 鉴权
158
-
159
- ### SKILL.md
160
- - [ ] 正文 < 500 行
161
- - [ ] 无重复 parameters 表(细节在 references)
162
- - [ ] 路由表覆盖所有 `references/*.md` 入口
163
- - [ ] Playbook 仅索引,步骤在 `core/playbooks.md`
164
-
165
- ### References
166
- - [ ] 每个 CLI 命令族有且仅有一个主 reference
167
- - [ ] 含 `--json-out` 示例(查询类)
168
- - [ ] 写命令含 `--commit` 与复核命令
169
- - [ ] 无断链(如 `accounts/open-account-by-media.md` 必须存在)
170
-
171
- ### 构建
172
- - [ ] `pnpm run build:tso:test` 后 `dist/skill/SKILL.md` 占位符已替换
173
- - [ ] `_meta.json` 版本与 package.json 一致
174
-
175
- ---
176
-
177
- ## 6. 反模式(Avoid)
178
-
179
- | 反模式 | 正确做法 |
180
- | ------ | -------- |
181
- | SKILL.md 内嵌 200 行 Playbook | 移至 `references/core/playbooks.md` |
182
- | Agent 用 `--json` 落大 JSON 到对话 | 一律 `--json-out` + 脚本读盘 |
183
- | 路由表缺文件 | 补文件或删链接 |
184
- | google-ads-rules 深链三层 | SKILL → `google-ads/rules/README.md` → 具体规则 |
185
- | HTML 模板假数据当示例 | 纲要 `.md` 定义维度,数据来自 CLI |
186
- | 凭记忆跨任务复用参数 | 每任务重读 `core/agent-conventions.md` § 加载纪律 |
187
- | 固定 `agents/` 与 skill 双源维护 | 用 `subagent-orchestration.md` + handoff snippets,主 Agent 自主决策 |
188
-
189
- ---
190
-
191
- ## 7. 相关仓库文档
192
-
193
- - `.cursor/rules/tutorial-05-core/skill-authoring.mdc` — 项目内 Skill 结构速查
194
- - `.cursor/rules/tutorial-10-best-practices.mdc` — CLI + Skill 同步等 12 条
195
- - `cso-cli/assets/siluzan-cso/SKILL.md` — 姊妹包参考(命令索引表风格)
196
- - https://agentskills.io/specification — 开放规范原文