siluzan-tso-cli 1.1.28-beta.2 → 1.1.28-beta.3

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.3),供内部测试使用。正式发布后安装命令将改为 `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,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.3",
4
+ "publishedAt": 1780909080666
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
 
@@ -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.3'
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.3"
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.3",
4
4
  "description": "Siluzan 广告账户管理 CLI — 查询账户、余额、消耗数据,管理绑定关系与充值。",
5
5
  "keywords": [
6
6
  "ad-account",