siluzan-tso-cli 1.1.23 → 1.1.24-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.
Files changed (36) hide show
  1. package/README.md +2 -1
  2. package/dist/index.js +357 -45
  3. package/dist/skill/AGENTS.md +3 -1
  4. package/dist/skill/SKILL.md +21 -4
  5. package/dist/skill/_meta.json +2 -2
  6. package/dist/skill/references/README.md +12 -0
  7. package/dist/skill/references/accounts/finance.md +4 -4
  8. package/dist/skill/references/analytics/account-analytics.md +42 -6
  9. package/dist/skill/references/analytics/facebook-analysis-guide.md +111 -0
  10. package/dist/skill/references/analytics/google-analysis-batch.md +12 -0
  11. package/dist/skill/references/analytics/rag.md +1 -1
  12. package/dist/skill/references/analytics/reporting.md +3 -3
  13. package/dist/skill/references/core/agent-conventions.md +8 -0
  14. package/dist/skill/references/core/playbooks.md +30 -4
  15. package/dist/skill/references/core/setup.md +5 -5
  16. package/dist/skill/references/core/skill-authoring.md +6 -2
  17. package/dist/skill/references/core/subagent-orchestration.md +148 -0
  18. package/dist/skill/references/core/workflows.md +1 -1
  19. package/dist/skill/report-templates/README.md +2 -1
  20. package/dist/skill/report-templates/REPORT-WORKFLOW.md +2 -2
  21. package/dist/skill/report-templates/google-inquiry-analysis.md +10 -0
  22. package/dist/skill/report-templates/meta-account-diagnosis-report.md +73 -0
  23. package/dist/skill/report-templates/meta-period-report.md +98 -14
  24. package/dist/skill/report-templates/okki-weekly-google-client.md +13 -0
  25. package/dist/skill/scripts/install.ps1 +3 -3
  26. package/dist/skill/scripts/install.sh +3 -3
  27. package/dist/skill/snippets/handoff-p5-batch.md +45 -0
  28. package/dist/skill/snippets/handoff-p6-okki.md +51 -0
  29. package/dist/skill/snippets/handoff-p7-inquiry.md +43 -0
  30. package/eval/cases/facebook-analysis-google-section-aliases.scenario.json +20 -0
  31. package/eval/cases/facebook-analysis-not-google-keywords.scenario.json +20 -0
  32. package/eval/cases/facebook-analysis-period-default.scenario.json +30 -0
  33. package/eval/cases/p4-fb-meta-period-report.scenario.json +21 -0
  34. package/eval/stub-fixtures/facebook-analysis.json +19 -0
  35. package/eval/stub-fixtures/meta-overview.json +10 -0
  36. package/package.json +1 -1
package/README.md CHANGED
@@ -43,7 +43,7 @@ HTML 报告模板引用以下 CDN:`cdn.tailwindcss.com`、`cdnjs.cloudflare.co
43
43
  在**用户的目标项目根目录**执行(根据用户使用的助手选择 `--ai`):
44
44
 
45
45
  ```bash
46
- npm install -g siluzan-tso-cli
46
+ npm install -g siluzan-tso-cli@beta
47
47
  siluzan-tso init --ai cursor # 写入 Cursor(默认)
48
48
  siluzan-tso init --ai cursor,claude # 同时写入多个平台
49
49
  siluzan-tso init --ai all # 写入所有支持的平台
@@ -51,6 +51,7 @@ siluzan-tso init -d /path/to/skills # 写入自定义目录
51
51
  siluzan-tso init --force # 强制覆盖已存在文件
52
52
  ```
53
53
 
54
+ > **注意**:当前为测试版(1.1.24-beta.3),供内部测试使用。正式发布后安装命令将改为 `npm install -g siluzan-tso-cli`。
54
55
 
55
56
  | 助手 | 建议 `--ai` |
56
57
  | ----------------------- | ------------------------------------ |
package/dist/index.js CHANGED
@@ -3328,7 +3328,7 @@ var DEFAULT_API_BASE;
3328
3328
  var init_defaults = __esm({
3329
3329
  "src/config/defaults.ts"() {
3330
3330
  "use strict";
3331
- DEFAULT_API_BASE = "https://tso-api.siluzan.com";
3331
+ DEFAULT_API_BASE = "https://tso-api-ci.siluzan.com";
3332
3332
  }
3333
3333
  });
3334
3334
 
@@ -4753,8 +4753,9 @@ var init_google_analysis = __esm({
4753
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"
4754
4754
  ];
4755
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"
4756
+ "// \u7ADE\u4E89\u6307\u6807\uFF1A`campaigns[].competitiveMetrics` \u4E3A **0~1 \u5C0F\u6570**\uFF08\u5982 `0.0999` = 9.99%\uFF09\uFF0C\u542B Top/AbsoluteTop/Content/ClickShare \u7B49\uFF1B\u65E0\u6570\u636E\u65F6\u4E3A `null`\u3002",
4757
+ "// \u884C\u9876 `searchImpressionShare` / `searchBudgetLostImpressionShare` / `searchRankLostImpressionShare` \u843D\u76D8\u524D\u5DF2\u5F52\u4E00\u4E3A **0~1 \u5C0F\u6570**\uFF08\u4E0E `competitiveMetrics` \u540C\u540D\u9879\u4E00\u81F4\uFF09\uFF1B\u5C55\u793A\u4E3A % \u65F6 \xD7100\u3002",
4758
+ "// `keywords[]` \u884C\u9876 3 \u9879\u4EFD\u989D\u540C\u6837\u4E3A 0~1 \u5C0F\u6570\uFF08\u65E0 `competitiveMetrics` \u65F6\u7531\u767E\u5206\u6570 \xF7100\uFF09\u3002"
4758
4759
  ];
4759
4760
  RATE_BEARING_SECTIONS = /* @__PURE__ */ new Set([
4760
4761
  "overview",
@@ -5009,6 +5010,61 @@ var init_sections = __esm({
5009
5010
  }
5010
5011
  });
5011
5012
 
5013
+ // src/commands/google-analysis/normalize-impression-shares.ts
5014
+ function normalizeShareValue(value, competitiveMetrics, field) {
5015
+ const cmVal = competitiveMetrics?.[field];
5016
+ if (typeof cmVal === "number" && Number.isFinite(cmVal)) {
5017
+ return cmVal;
5018
+ }
5019
+ if (Math.abs(value) > 1) {
5020
+ return value / 100;
5021
+ }
5022
+ return value;
5023
+ }
5024
+ function normalizeLegacyShareOnRow(row) {
5025
+ const rawCm = row.competitiveMetrics;
5026
+ const competitiveMetrics = rawCm !== null && typeof rawCm === "object" && !Array.isArray(rawCm) ? rawCm : null;
5027
+ for (const field of LEGACY_ROW_IMPRESSION_SHARE_FIELDS) {
5028
+ const v = row[field];
5029
+ if (typeof v !== "number" || !Number.isFinite(v)) continue;
5030
+ row[field] = normalizeShareValue(v, competitiveMetrics, field);
5031
+ }
5032
+ }
5033
+ function normalizeRowsInArray(rows) {
5034
+ if (!Array.isArray(rows)) return;
5035
+ for (const row of rows) {
5036
+ if (row !== null && typeof row === "object" && !Array.isArray(row)) {
5037
+ normalizeLegacyShareOnRow(row);
5038
+ }
5039
+ }
5040
+ }
5041
+ function normalizeImpressionShareScales(payload, section) {
5042
+ if (!SECTIONS_WITH_LEGACY_ROW_SHARES.has(section)) return payload;
5043
+ if (payload === null || typeof payload !== "object") return payload;
5044
+ const root = payload;
5045
+ if (section === "campaigns") {
5046
+ normalizeRowsInArray(root.campaigns);
5047
+ } else if (section === "keywords") {
5048
+ normalizeRowsInArray(root.keywords);
5049
+ }
5050
+ return payload;
5051
+ }
5052
+ var LEGACY_ROW_IMPRESSION_SHARE_FIELDS, SECTIONS_WITH_LEGACY_ROW_SHARES;
5053
+ var init_normalize_impression_shares = __esm({
5054
+ "src/commands/google-analysis/normalize-impression-shares.ts"() {
5055
+ "use strict";
5056
+ LEGACY_ROW_IMPRESSION_SHARE_FIELDS = [
5057
+ "searchImpressionShare",
5058
+ "searchBudgetLostImpressionShare",
5059
+ "searchRankLostImpressionShare"
5060
+ ];
5061
+ SECTIONS_WITH_LEGACY_ROW_SHARES = /* @__PURE__ */ new Set([
5062
+ "campaigns",
5063
+ "keywords"
5064
+ ]);
5065
+ }
5066
+ });
5067
+
5012
5068
  // src/commands/google-analysis/normalize-rates.ts
5013
5069
  function divIfFiniteNumber(v) {
5014
5070
  return typeof v === "number" && Number.isFinite(v) ? v / 100 : v;
@@ -100824,6 +100880,7 @@ async function fetchSectionPayload(def, opts, config, id) {
100824
100880
  );
100825
100881
  let payload = stripLegacyGoogleFieldsIfV2Present(data);
100826
100882
  payload = normalizeRateScales(payload, def.name);
100883
+ payload = normalizeImpressionShareScales(payload, def.name);
100827
100884
  payload = normalizeMoneyScales(payload, def.name);
100828
100885
  if (def.name === "overview") {
100829
100886
  payload = normalizeOverviewPayload(payload);
@@ -100871,6 +100928,7 @@ var init_fetch = __esm({
100871
100928
  init_auth();
100872
100929
  init_strip_legacy_google_fields();
100873
100930
  init_sections();
100931
+ init_normalize_impression_shares();
100874
100932
  init_normalize_rates();
100875
100933
  init_normalize_money();
100876
100934
  init_normalize_overview();
@@ -100905,14 +100963,14 @@ function computeBackoffMs(attempt, policy) {
100905
100963
  return capped + jitter;
100906
100964
  }
100907
100965
  function sleep3(ms, signal) {
100908
- return new Promise((resolve13, reject) => {
100966
+ return new Promise((resolve14, reject) => {
100909
100967
  if (signal?.aborted) {
100910
100968
  reject(new Error("retry sleep aborted"));
100911
100969
  return;
100912
100970
  }
100913
100971
  const timer = setTimeout(() => {
100914
100972
  signal?.removeEventListener("abort", onAbort);
100915
- resolve13();
100973
+ resolve14();
100916
100974
  }, ms);
100917
100975
  const onAbort = () => {
100918
100976
  clearTimeout(timer);
@@ -102514,6 +102572,7 @@ var init_google_analysis2 = __esm({
102514
102572
  init_sections();
102515
102573
  init_fetch();
102516
102574
  init_normalize_rates();
102575
+ init_normalize_impression_shares();
102517
102576
  init_translate_fields();
102518
102577
  init_register_cli();
102519
102578
  }
@@ -102531,7 +102590,7 @@ var init_google_analysis3 = __esm({
102531
102590
  init_dist();
102532
102591
  init_dist();
102533
102592
  import * as fs14 from "fs";
102534
- import * as path21 from "path";
102593
+ import * as path22 from "path";
102535
102594
  import { fileURLToPath as fileURLToPath4 } from "url";
102536
102595
  import { Command } from "commander";
102537
102596
 
@@ -103016,10 +103075,10 @@ function deriveWebUrl(apiBaseUrl) {
103016
103075
  }
103017
103076
  var TSO_UMI_ROUTE_PREFIX = "/v3umijs/tso";
103018
103077
  function buildTsoPageWebUrl(webUrl, tsoSubRoute) {
103019
- const path22 = tsoSubRoute.replace(/^\/+/, "");
103020
- const umiPath = `${TSO_UMI_ROUTE_PREFIX}/${path22}`;
103078
+ const path23 = tsoSubRoute.replace(/^\/+/, "");
103079
+ const umiPath = `${TSO_UMI_ROUTE_PREFIX}/${path23}`;
103021
103080
  const q = new URLSearchParams({ tso: umiPath });
103022
- return `${webUrl.replace(/\/+$/, "")}/v3/foreign_trade/tso/${path22}?${q.toString()}`;
103081
+ return `${webUrl.replace(/\/+$/, "")}/v3/foreign_trade/tso/${path23}?${q.toString()}`;
103023
103082
  }
103024
103083
  function cmdConfigShow() {
103025
103084
  const shared = readSharedConfig();
@@ -110193,7 +110252,7 @@ function cloneKeywordBlockShell(block) {
110193
110252
  delete shell["matchTypeV2"];
110194
110253
  return shell;
110195
110254
  }
110196
- function splitKeywordsForBatchJobBlockIfMixed(block, path22, warnings) {
110255
+ function splitKeywordsForBatchJobBlockIfMixed(block, path23, warnings) {
110197
110256
  const declaredUi = matchTypeV2ToUi(readBlockMatchTypeRaw(block));
110198
110257
  const entries = collectKeywordEntriesFromBlock(block);
110199
110258
  if (entries.length === 0) return [block];
@@ -110212,7 +110271,7 @@ function splitKeywordsForBatchJobBlockIfMixed(block, path22, warnings) {
110212
110271
  (ui) => matchTypeUiToV2(ui)
110213
110272
  );
110214
110273
  warnings.push(
110215
- `${path22} \u542B\u591A\u79CD\u5339\u914D\u7C7B\u578B\uFF0C\u5DF2\u81EA\u52A8\u62C6\u5206\u4E3A ${groups.size} \u4E2A KeywordsForBatchJob \u5757\uFF08${labels.join("\u3001")}\uFF09`
110274
+ `${path23} \u542B\u591A\u79CD\u5339\u914D\u7C7B\u578B\uFF0C\u5DF2\u81EA\u52A8\u62C6\u5206\u4E3A ${groups.size} \u4E2A KeywordsForBatchJob \u5757\uFF08${labels.join("\u3001")}\uFF09`
110216
110275
  );
110217
110276
  const shell = cloneKeywordBlockShell(block);
110218
110277
  const splitBlocks = [];
@@ -110232,16 +110291,16 @@ function splitKeywordsForBatchJobBlockIfMixed(block, path22, warnings) {
110232
110291
  }
110233
110292
  return splitBlocks;
110234
110293
  }
110235
- function validateKeywordCore(core, path22, errors, lengthViolations) {
110294
+ function validateKeywordCore(core, path23, errors, lengthViolations) {
110236
110295
  const trimmed = core.trim();
110237
110296
  if (!trimmed) {
110238
- errors.push(`${path22} \u5173\u952E\u8BCD\u8BCD\u5E72\u4E0D\u80FD\u4E3A\u7A7A`);
110297
+ errors.push(`${path23} \u5173\u952E\u8BCD\u8BCD\u5E72\u4E0D\u80FD\u4E3A\u7A7A`);
110239
110298
  return false;
110240
110299
  }
110241
110300
  if (trimmed.length > GOOGLE_KEYWORD_MAX_CORE_LENGTH) {
110242
110301
  if (lengthViolations) {
110243
110302
  pushLengthViolation(lengthViolations, {
110244
- path: path22,
110303
+ path: path23,
110245
110304
  field: "KeywordText",
110246
110305
  kind: "keyword_core",
110247
110306
  limit: GOOGLE_KEYWORD_MAX_CORE_LENGTH,
@@ -110251,13 +110310,13 @@ function validateKeywordCore(core, path22, errors, lengthViolations) {
110251
110310
  });
110252
110311
  }
110253
110312
  errors.push(
110254
- `${path22} \u5173\u952E\u8BCD\u8BCD\u5E72\u8D85\u8FC7 ${GOOGLE_KEYWORD_MAX_CORE_LENGTH} \u5B57\u7B26\uFF08\u5F53\u524D ${trimmed.length}\uFF09\uFF1A"${trimmed}"`
110313
+ `${path23} \u5173\u952E\u8BCD\u8BCD\u5E72\u8D85\u8FC7 ${GOOGLE_KEYWORD_MAX_CORE_LENGTH} \u5B57\u7B26\uFF08\u5F53\u524D ${trimmed.length}\uFF09\uFF1A"${trimmed}"`
110255
110314
  );
110256
110315
  return false;
110257
110316
  }
110258
110317
  if (!VALID_CORE_REGEX.test(trimmed)) {
110259
110318
  errors.push(
110260
- `${path22} \u542B Google \u4E0D\u5141\u8BB8\u7684\u5B57\u7B26\uFF08\u8BCD\u5E72\u4EC5\u5141\u8BB8\u5B57\u6BCD/\u6570\u5B57/\u7A7A\u683C/\u8FDE\u5B57\u7B26/\u53E5\u70B9\uFF09\uFF1A"${trimmed.slice(0, 40)}${trimmed.length > 40 ? "\u2026" : ""}"`
110319
+ `${path23} \u542B Google \u4E0D\u5141\u8BB8\u7684\u5B57\u7B26\uFF08\u8BCD\u5E72\u4EC5\u5141\u8BB8\u5B57\u6BCD/\u6570\u5B57/\u7A7A\u683C/\u8FDE\u5B57\u7B26/\u53E5\u70B9\uFF09\uFF1A"${trimmed.slice(0, 40)}${trimmed.length > 40 ? "\u2026" : ""}"`
110261
110320
  );
110262
110321
  return false;
110263
110322
  }
@@ -110314,24 +110373,24 @@ function canonicalizeKeywordBatchBlock(block, resolvedMatchV2, normalizedTexts,
110314
110373
  }
110315
110374
  }
110316
110375
  }
110317
- function pushKeywordAutoFixWarning(warnings, path22, fieldLabel, trimmedRaw, formatted, declaredUi, beforeUi, matchType, inferredMatchType) {
110376
+ function pushKeywordAutoFixWarning(warnings, path23, fieldLabel, trimmedRaw, formatted, declaredUi, beforeUi, matchType, inferredMatchType) {
110318
110377
  if (inferredMatchType) {
110319
110378
  warnings.push(
110320
- `${path22}.${fieldLabel} \u672A\u6307\u5B9A MatchTypeV2\uFF0C\u5DF2\u6309\u8BCD\u9762\u63A8\u65AD\u4E3A ${matchTypeUiToV2(matchType)}`
110379
+ `${path23}.${fieldLabel} \u672A\u6307\u5B9A MatchTypeV2\uFF0C\u5DF2\u6309\u8BCD\u9762\u63A8\u65AD\u4E3A ${matchTypeUiToV2(matchType)}`
110321
110380
  );
110322
110381
  return;
110323
110382
  }
110324
110383
  if (declaredUi && beforeUi !== declaredUi) {
110325
110384
  warnings.push(
110326
- `${path22}.${fieldLabel} MatchTypeV2=${matchTypeUiToV2(declaredUi)} \u4E0E\u8BCD\u9762\u4E0D\u4E00\u81F4\uFF0C\u5DF2\u81EA\u52A8\u4FEE\u590D\u4E3A\uFF1A${formatted}`
110385
+ `${path23}.${fieldLabel} MatchTypeV2=${matchTypeUiToV2(declaredUi)} \u4E0E\u8BCD\u9762\u4E0D\u4E00\u81F4\uFF0C\u5DF2\u81EA\u52A8\u4FEE\u590D\u4E3A\uFF1A${formatted}`
110327
110386
  );
110328
110387
  return;
110329
110388
  }
110330
110389
  if (formatted !== trimmedRaw) {
110331
- warnings.push(`${path22}.${fieldLabel} \u5DF2\u81EA\u52A8\u4FEE\u590D\u8BCD\u9762\uFF1A${trimmedRaw} \u2192 ${formatted}`);
110390
+ warnings.push(`${path23}.${fieldLabel} \u5DF2\u81EA\u52A8\u4FEE\u590D\u8BCD\u9762\uFF1A${trimmedRaw} \u2192 ${formatted}`);
110332
110391
  }
110333
110392
  }
110334
- function normalizeKeywordTextList(texts, matchTypeRaw, path22, fieldLabel, errors, warnings, lengthViolations) {
110393
+ function normalizeKeywordTextList(texts, matchTypeRaw, path23, fieldLabel, errors, warnings, lengthViolations) {
110335
110394
  const declaredUi = matchTypeV2ToUi(matchTypeRaw);
110336
110395
  const normalized = [];
110337
110396
  const seen = /* @__PURE__ */ new Set();
@@ -110339,7 +110398,7 @@ function normalizeKeywordTextList(texts, matchTypeRaw, path22, fieldLabel, error
110339
110398
  for (let k = 0; k < texts.length; k++) {
110340
110399
  const raw = texts[k];
110341
110400
  if (typeof raw !== "string" || !raw.trim()) {
110342
- errors.push(`${path22}.${fieldLabel}[${k}] \u5FC5\u987B\u662F\u975E\u7A7A\u5B57\u7B26\u4E32`);
110401
+ errors.push(`${path23}.${fieldLabel}[${k}] \u5FC5\u987B\u662F\u975E\u7A7A\u5B57\u7B26\u4E32`);
110343
110402
  continue;
110344
110403
  }
110345
110404
  const trimmedRaw = collapseDuplicateSpacesInKeywordText(raw.trim());
@@ -110347,7 +110406,7 @@ function normalizeKeywordTextList(texts, matchTypeRaw, path22, fieldLabel, error
110347
110406
  const { formatted, matchType, inferredMatchType } = normalizeKeywordSurface(trimmedRaw, matchTypeRaw);
110348
110407
  pushKeywordAutoFixWarning(
110349
110408
  warnings,
110350
- path22,
110409
+ path23,
110351
110410
  fieldLabel,
110352
110411
  trimmedRaw,
110353
110412
  formatted,
@@ -110357,10 +110416,10 @@ function normalizeKeywordTextList(texts, matchTypeRaw, path22, fieldLabel, error
110357
110416
  inferredMatchType
110358
110417
  );
110359
110418
  const core = unwrapKeywordDisplayTextForEdit(formatted);
110360
- if (!validateKeywordCore(core, `${path22}.${fieldLabel}[${k}]`, errors, lengthViolations)) continue;
110419
+ if (!validateKeywordCore(core, `${path23}.${fieldLabel}[${k}]`, errors, lengthViolations)) continue;
110361
110420
  const dedupeKey = `${matchTypeUiToV2(matchType)}:${core.toLowerCase()}`;
110362
110421
  if (seen.has(dedupeKey)) {
110363
- warnings.push(`${path22}.${fieldLabel}[${k}] \u4E0E\u540C\u7EC4\u91CD\u590D\uFF0C\u5DF2\u8DF3\u8FC7\uFF1A${formatted}`);
110422
+ warnings.push(`${path23}.${fieldLabel}[${k}] \u4E0E\u540C\u7EC4\u91CD\u590D\uFF0C\u5DF2\u8DF3\u8FC7\uFF1A${formatted}`);
110364
110423
  continue;
110365
110424
  }
110366
110425
  seen.add(dedupeKey);
@@ -110369,12 +110428,12 @@ function normalizeKeywordTextList(texts, matchTypeRaw, path22, fieldLabel, error
110369
110428
  }
110370
110429
  return { normalized, resolvedUi };
110371
110430
  }
110372
- function normalizeKeywordsForBatchJobBlock(block, path22, errors, warnings, lengthViolations) {
110431
+ function normalizeKeywordsForBatchJobBlock(block, path23, errors, warnings, lengthViolations) {
110373
110432
  const matchTypeRaw = readBlockMatchTypeRaw(block);
110374
110433
  const declaredUi = matchTypeV2ToUi(matchTypeRaw);
110375
110434
  if (matchTypeRaw !== void 0 && declaredUi === null) {
110376
110435
  errors.push(
110377
- `${path22}.MatchTypeV2 \u65E0\u6548\uFF08${String(matchTypeRaw)}\uFF09\uFF0C\u5408\u6CD5\u503C\uFF1ABROAD | PHRASE | EXACT`
110436
+ `${path23}.MatchTypeV2 \u65E0\u6548\uFF08${String(matchTypeRaw)}\uFF09\uFF0C\u5408\u6CD5\u503C\uFF1ABROAD | PHRASE | EXACT`
110378
110437
  );
110379
110438
  return;
110380
110439
  }
@@ -110389,7 +110448,7 @@ function normalizeKeywordsForBatchJobBlock(block, path22, errors, warnings, leng
110389
110448
  const result = normalizeKeywordTextList(
110390
110449
  texts,
110391
110450
  matchTypeRaw,
110392
- path22,
110451
+ path23,
110393
110452
  "KeywordText",
110394
110453
  errors,
110395
110454
  warnings,
@@ -110398,7 +110457,7 @@ function normalizeKeywordsForBatchJobBlock(block, path22, errors, warnings, leng
110398
110457
  normalizedTexts = result.normalized;
110399
110458
  if (result.resolvedUi) resolvedUi = result.resolvedUi;
110400
110459
  if (normalizedTexts.length === 0 && texts.length > 0) {
110401
- errors.push(`${path22}.KeywordText \u7ECF\u6821\u9A8C\u540E\u65E0\u6709\u6548\u5173\u952E\u8BCD\uFF0C\u8BF7\u4FEE\u6B63\u8BCD\u9762\u6216 MatchTypeV2`);
110460
+ errors.push(`${path23}.KeywordText \u7ECF\u6821\u9A8C\u540E\u65E0\u6709\u6548\u5173\u952E\u8BCD\uFF0C\u8BF7\u4FEE\u6B63\u8BCD\u9762\u6216 MatchTypeV2`);
110402
110461
  }
110403
110462
  }
110404
110463
  if (hasItems && items) {
@@ -110406,12 +110465,12 @@ function normalizeKeywordsForBatchJobBlock(block, path22, errors, warnings, leng
110406
110465
  for (let k = 0; k < items.length; k++) {
110407
110466
  const item = items[k];
110408
110467
  if (!item || typeof item !== "object") {
110409
- errors.push(`${path22}.Items[${k}] \u5FC5\u987B\u662F\u5BF9\u8C61`);
110468
+ errors.push(`${path23}.Items[${k}] \u5FC5\u987B\u662F\u5BF9\u8C61`);
110410
110469
  continue;
110411
110470
  }
110412
110471
  const raw = readItemText(item);
110413
110472
  if (raw === null || !raw.trim()) {
110414
- errors.push(`${path22}.Items[${k}].Text \u5FC5\u987B\u662F\u975E\u7A7A\u5B57\u7B26\u4E32`);
110473
+ errors.push(`${path23}.Items[${k}].Text \u5FC5\u987B\u662F\u975E\u7A7A\u5B57\u7B26\u4E32`);
110415
110474
  continue;
110416
110475
  }
110417
110476
  const trimmedRaw = collapseDuplicateSpacesInKeywordText(raw.trim());
@@ -110419,7 +110478,7 @@ function normalizeKeywordsForBatchJobBlock(block, path22, errors, warnings, leng
110419
110478
  const { formatted, matchType, inferredMatchType } = normalizeKeywordSurface(trimmedRaw, matchTypeRaw);
110420
110479
  pushKeywordAutoFixWarning(
110421
110480
  warnings,
110422
- path22,
110481
+ path23,
110423
110482
  `Items[${k}].Text`,
110424
110483
  trimmedRaw,
110425
110484
  formatted,
@@ -110429,10 +110488,10 @@ function normalizeKeywordsForBatchJobBlock(block, path22, errors, warnings, leng
110429
110488
  inferredMatchType
110430
110489
  );
110431
110490
  const core = unwrapKeywordDisplayTextForEdit(formatted);
110432
- if (!validateKeywordCore(core, `${path22}.Items[${k}].Text`, errors, lengthViolations)) continue;
110491
+ if (!validateKeywordCore(core, `${path23}.Items[${k}].Text`, errors, lengthViolations)) continue;
110433
110492
  const dedupeKey = `${matchTypeUiToV2(matchType)}:${core.toLowerCase()}`;
110434
110493
  if (seen.has(dedupeKey)) {
110435
- warnings.push(`${path22}.Items[${k}].Text \u4E0E\u540C\u7EC4\u91CD\u590D\uFF0C\u5DF2\u8DF3\u8FC7\uFF1A${formatted}`);
110494
+ warnings.push(`${path23}.Items[${k}].Text \u4E0E\u540C\u7EC4\u91CD\u590D\uFF0C\u5DF2\u8DF3\u8FC7\uFF1A${formatted}`);
110436
110495
  continue;
110437
110496
  }
110438
110497
  seen.add(dedupeKey);
@@ -113031,12 +113090,12 @@ function compareCampaignCreateToLive(cfg, campaignId, live, meta) {
113031
113090
  for (let ki = 0; ki < texts.length; ki++) {
113032
113091
  const t = texts[ki];
113033
113092
  if (typeof t !== "string" || !t.trim()) continue;
113034
- const path22 = `${groupPath}.KeywordsForBatchJob[${bi}].KeywordText[${ki}]`;
113093
+ const path23 = `${groupPath}.KeywordsForBatchJob[${bi}].KeywordText[${ki}]`;
113035
113094
  const key = keywordKey(t, matchTypeV2);
113036
113095
  if (!liveKwKeys.has(key)) {
113037
113096
  missing.push({
113038
113097
  layer: "keyword",
113039
- path: path22,
113098
+ path: path23,
113040
113099
  adGroupName: groupName,
113041
113100
  plannedContent: `\u8BCD\u9762: ${t} | \u5339\u914D: ${matchTypeV2}`,
113042
113101
  liveNote: `\u540C\u7EC4\u5DF2\u6709 ${liveKwInGroup.length} \u6761\u5173\u952E\u8BCD\uFF0C\u65E0\u952E ${key}`,
@@ -113054,12 +113113,12 @@ function compareCampaignCreateToLive(cfg, campaignId, live, meta) {
113054
113113
  for (let ai = 0; ai < ads.length; ai++) {
113055
113114
  const ad = asRecord(ads[ai]);
113056
113115
  if (!ad) continue;
113057
- const path22 = `${groupPath}.AdsForBatchJob[${ai}]`;
113116
+ const path23 = `${groupPath}.AdsForBatchJob[${ai}]`;
113058
113117
  const primary = pickString(ad["headlinePart1"], ad["AdTitle"]);
113059
113118
  if (!primary) {
113060
113119
  missing.push({
113061
113120
  layer: "ad",
113062
- path: path22,
113121
+ path: path23,
113063
113122
  adGroupName: groupName,
113064
113123
  plannedContent: `AdsForBatchJob[${ai}]\uFF08\u7F3A\u5C11 headlinePart1\uFF0C\u65E0\u6CD5\u6BD4\u5BF9\uFF09`,
113065
113124
  liveNote: `\u540C\u7EC4\u5DF2\u6709 ${liveAdsInGroup.length} \u6761\u521B\u610F`,
@@ -113072,7 +113131,7 @@ function compareCampaignCreateToLive(cfg, campaignId, live, meta) {
113072
113131
  if (!found) {
113073
113132
  missing.push({
113074
113133
  layer: "ad",
113075
- path: path22,
113134
+ path: path23,
113076
113135
  adGroupName: groupName,
113077
113136
  plannedContent: `RSA \u9996\u6807\u9898: ${primary}${finalUrl ? ` | \u843D\u5730\u9875: ${finalUrl}` : ""}`,
113078
113137
  liveNote: `\u540C\u7EC4\u5DF2\u6709 ${liveAdsInGroup.length} \u6761 RSA\uFF0C\u65E0\u6B64\u9996\u6807\u9898`,
@@ -114212,13 +114271,13 @@ function pmaxChannelTypesUrl(googleApiUrl) {
114212
114271
  }
114213
114272
  function pmaxCampaignUrl(googleApiUrl, accountId, campaignId) {
114214
114273
  const base = googleApiUrl.replace(/\/$/, "");
114215
- const path22 = `${base}/accounts/${accountId}/campaign/pmax`;
114216
- return campaignId ? `${path22}/${campaignId}` : path22;
114274
+ const path23 = `${base}/accounts/${accountId}/campaign/pmax`;
114275
+ return campaignId ? `${path23}/${campaignId}` : path23;
114217
114276
  }
114218
114277
  function pmaxAssetGroupUrl(googleApiUrl, accountId, assetGroupId, suffix) {
114219
114278
  const base = googleApiUrl.replace(/\/$/, "");
114220
- const path22 = `${base}/accounts/${accountId}/campaign/pmax/asset-group/${assetGroupId}`;
114221
- return suffix ? `${path22}/${suffix.replace(/^\//, "")}` : path22;
114279
+ const path23 = `${base}/accounts/${accountId}/campaign/pmax/asset-group/${assetGroupId}`;
114280
+ return suffix ? `${path23}/${suffix.replace(/^\//, "")}` : path23;
114222
114281
  }
114223
114282
  function pmaxCampaignAssetGroupUrl(googleApiUrl, accountId, campaignId) {
114224
114283
  const base = googleApiUrl.replace(/\/$/, "");
@@ -120900,13 +120959,265 @@ function register25(program2) {
120900
120959
  // src/index.ts
120901
120960
  init_google_analysis3();
120902
120961
  init_google_analysis_batch();
120962
+
120963
+ // src/commands/facebook-analysis/sections.ts
120964
+ var FACEBOOK_SECTIONS = [
120965
+ {
120966
+ name: "overview",
120967
+ description: "\u603B\u89C8 OverviewSectionData\uFF08\u542B currentPeriod / previousPeriod \u73AF\u6BD4\uFF09",
120968
+ segment: "OverviewSectionData"
120969
+ },
120970
+ {
120971
+ name: "ad-sets",
120972
+ description: "\u5E7F\u544A\u7EC4 AdSetSectionData\uFF08Meta Ad Set\uFF0C\u5B57\u6BB5 adGroups\uFF09",
120973
+ segment: "AdSetSectionData"
120974
+ },
120975
+ {
120976
+ name: "platform",
120977
+ description: "\u6295\u653E\u5E73\u53F0 PlatformSectionData\uFF08facebook / instagram \u7B49\uFF09",
120978
+ segment: "PlatformSectionData"
120979
+ },
120980
+ {
120981
+ name: "country",
120982
+ description: "\u56FD\u5BB6 CountrySectionData\uFF08\u53EF\u6309 spend \u964D\u5E8F limit\uFF09",
120983
+ segment: "CountrySectionData",
120984
+ countryLimitOption: true
120985
+ },
120986
+ {
120987
+ name: "audience",
120988
+ description: "\u53D7\u4F17 AudienceSectionData\uFF08\u5E74\u9F84 \xD7 \u6027\u522B\uFF09",
120989
+ segment: "AudienceSectionData"
120990
+ },
120991
+ {
120992
+ name: "creative",
120993
+ description: "\u521B\u610F CreativeSectionData\uFF08\u9ED8\u8BA4\u521B\u610F\u62A5\u544A\uFF0C\u6240\u6709\u8D26\u6237\u53EF\u7528\uFF09",
120994
+ segment: "CreativeSectionData"
120995
+ },
120996
+ {
120997
+ name: "material",
120998
+ description: "\u7D20\u6750 MaterialSectionData\uFF08\u4EC5 Dynamic Creative\uFF1B\u6807\u51C6\u8D26\u6237\u5E38\u4E3A\u7A7A\uFF09",
120999
+ segment: "MaterialSectionData"
121000
+ }
121001
+ ];
121002
+ var FACEBOOK_SECTION_NAMES = FACEBOOK_SECTIONS.map((s) => s.name);
121003
+ var FACEBOOK_SECTION_ALIASES = {
121004
+ campaigns: "ad-sets",
121005
+ "ad-groups": "ad-sets",
121006
+ geographic: "country",
121007
+ geo: "country",
121008
+ devices: "platform",
121009
+ network: "platform",
121010
+ networks: "platform",
121011
+ ads: "creative",
121012
+ materials: "material",
121013
+ "asset-images": "material",
121014
+ videos: "material"
121015
+ };
121016
+
121017
+ // src/commands/facebook-analysis/fetch.ts
121018
+ init_auth();
121019
+ function normalizeFacebookAccountId(raw) {
121020
+ const t = raw.trim();
121021
+ if (!t) {
121022
+ console.error("\n\u274C --account \u4E0D\u80FD\u4E3A\u7A7A\u3002\n");
121023
+ process.exit(1);
121024
+ }
121025
+ const digits = t.startsWith("act_") ? t.slice(4) : t;
121026
+ if (!/^\d+$/.test(digits)) {
121027
+ console.error(
121028
+ "\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"
121029
+ );
121030
+ process.exit(1);
121031
+ }
121032
+ return { apiId: `act_${digits}`, manifestId: digits };
121033
+ }
121034
+ function resolveFacebookDateRange(start, end) {
121035
+ if (start && end) return { startDate: start, endDate: end };
121036
+ if (!start && !end) {
121037
+ const endD = /* @__PURE__ */ new Date();
121038
+ endD.setDate(endD.getDate() - 1);
121039
+ const startD = new Date(endD);
121040
+ startD.setDate(startD.getDate() - 6);
121041
+ const fmt2 = (d) => d.toISOString().slice(0, 10);
121042
+ return { startDate: fmt2(startD), endDate: fmt2(endD) };
121043
+ }
121044
+ 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");
121045
+ process.exit(1);
121046
+ }
121047
+ function facebookReportingUrl(config, apiId, segment, query) {
121048
+ const q = query ? query.startsWith("?") ? query : `?${query}` : "";
121049
+ return `${config.apiBaseUrl}/reporting/media-account/FacebookAds/${encodeURIComponent(apiId)}/${segment}${q}`;
121050
+ }
121051
+ async function fetchFacebookSectionPayload(def, opts, config, apiId) {
121052
+ const { startDate, endDate } = resolveFacebookDateRange(opts.start, opts.end);
121053
+ const params = new URLSearchParams({ startDate, endDate });
121054
+ if (def.countryLimitOption && opts.limit !== void 0 && Number.isFinite(opts.limit)) {
121055
+ params.set("limit", String(Math.max(1, Math.floor(opts.limit))));
121056
+ }
121057
+ const url = facebookReportingUrl(config, apiId, def.segment, params.toString());
121058
+ return apiFetch2(url, config, {}, opts.verbose ?? false);
121059
+ }
121060
+ function endpointHintForFacebookSection(def, apiId) {
121061
+ return `GET \u2026/FacebookAds/${apiId}/${def.segment}`;
121062
+ }
121063
+
121064
+ // src/commands/facebook-analysis/run-batch.ts
121065
+ init_auth();
121066
+ import * as path21 from "path";
121067
+ import { performance as performance5 } from "perf_hooks";
121068
+ init_version();
121069
+
121070
+ // src/commands/facebook-analysis/resolve-sections.ts
121071
+ function normalizeSectionToken(raw) {
121072
+ const t = raw.trim();
121073
+ if (!t) return null;
121074
+ if (FACEBOOK_SECTION_NAMES.includes(t)) {
121075
+ return t;
121076
+ }
121077
+ return FACEBOOK_SECTION_ALIASES[t] ?? null;
121078
+ }
121079
+ function resolveSectionList2(sections, exclude) {
121080
+ const splitClean = (s) => (s ?? "").split(",").map((x) => x.trim()).filter(Boolean);
121081
+ const include = splitClean(sections);
121082
+ const dropRaw = splitClean(exclude);
121083
+ const unknown = [...include, ...dropRaw].filter((n) => normalizeSectionToken(n) === null);
121084
+ if (unknown.length > 0) {
121085
+ console.error(
121086
+ `
121087
+ \u274C \u672A\u77E5\u7684 section \u540D\u79F0\uFF1A${unknown.join(", ")}
121088
+ \u53EF\u9009\uFF1A${FACEBOOK_SECTION_NAMES.join(", ")}
121089
+ Google \u522B\u540D\uFF1A${Object.keys(FACEBOOK_SECTION_ALIASES).join(", ")}
121090
+ `
121091
+ );
121092
+ process.exit(1);
121093
+ }
121094
+ const includeCanonical = new Set(
121095
+ include.map((n) => normalizeSectionToken(n)).filter(Boolean)
121096
+ );
121097
+ const dropCanonical = new Set(
121098
+ dropRaw.map((n) => normalizeSectionToken(n)).filter(Boolean)
121099
+ );
121100
+ const base = include.length > 0 ? FACEBOOK_SECTIONS.filter((s) => includeCanonical.has(s.name)) : [...FACEBOOK_SECTIONS];
121101
+ return base.filter((s) => !dropCanonical.has(s.name));
121102
+ }
121103
+
121104
+ // src/commands/facebook-analysis/run-batch.ts
121105
+ async function runWithConcurrency2(tasks, concurrency) {
121106
+ const results = new Array(tasks.length);
121107
+ let cursor = 0;
121108
+ const workers = new Array(Math.max(1, Math.min(concurrency, tasks.length))).fill(0).map(async () => {
121109
+ while (true) {
121110
+ const i = cursor++;
121111
+ if (i >= tasks.length) return;
121112
+ results[i] = await tasks[i]();
121113
+ }
121114
+ });
121115
+ await Promise.all(workers);
121116
+ return results;
121117
+ }
121118
+ async function runAllFacebookSections(opts) {
121119
+ const { apiId, manifestId } = normalizeFacebookAccountId(opts.account);
121120
+ const config = loadConfig(opts.token);
121121
+ if (!opts.jsonOut?.trim()) {
121122
+ console.error("\n\u274C \u5FC5\u987B\u4F20 --json-out <\u76EE\u5F55>\uFF08\u5404\u7EF4\u5EA6\u843D\u76D8\u540E\u7531 report-manifest \u7D22\u5F15\uFF09\u3002\n");
121123
+ process.exit(1);
121124
+ }
121125
+ const targets = resolveSectionList2(opts.sections, opts.exclude);
121126
+ if (targets.length === 0) {
121127
+ console.error("\n\u274C \u6CA1\u6709\u8981\u6267\u884C\u7684 section\uFF08\u8BF7\u68C0\u67E5 --sections / --exclude\uFF09\u3002\n");
121128
+ process.exit(1);
121129
+ }
121130
+ const { startDate, endDate } = resolveFacebookDateRange(opts.start, opts.end);
121131
+ const concurrency = Math.max(
121132
+ 1,
121133
+ Math.min(typeof opts.concurrency === "number" ? opts.concurrency : 5, 16)
121134
+ );
121135
+ const cliVersion = getCurrentVersion2();
121136
+ const overallT0 = performance5.now();
121137
+ const tasks = targets.map((def) => async () => {
121138
+ const t0 = performance5.now();
121139
+ const hint = endpointHintForFacebookSection(def, apiId);
121140
+ try {
121141
+ const payload = await fetchFacebookSectionPayload(def, opts, config, apiId);
121142
+ const summary2 = await writeReportAnalysisSnapshot({
121143
+ snapshotDir: opts.jsonOut,
121144
+ section: def.name,
121145
+ accountId: manifestId,
121146
+ dateRange: { start: startDate, end: endDate },
121147
+ payload,
121148
+ cliVersion
121149
+ });
121150
+ return {
121151
+ section: def.name,
121152
+ ok: true,
121153
+ elapsedMs: performance5.now() - t0,
121154
+ file: summary2.writtenFiles[0],
121155
+ endpointHint: hint
121156
+ };
121157
+ } catch (err) {
121158
+ return {
121159
+ section: def.name,
121160
+ ok: false,
121161
+ elapsedMs: performance5.now() - t0,
121162
+ error: err instanceof Error ? err.message : String(err),
121163
+ endpointHint: hint
121164
+ };
121165
+ }
121166
+ });
121167
+ const results = await runWithConcurrency2(tasks, concurrency);
121168
+ const succeeded = results.filter((r) => r.ok).length;
121169
+ const failed = results.length - succeeded;
121170
+ const absoluteSnapshotDir = path21.resolve(opts.jsonOut);
121171
+ const summary = {
121172
+ kind: "siluzan-tso-facebook-analysis-snapshot-batch",
121173
+ absoluteSnapshotDir,
121174
+ manifestFile: reportManifestFile(manifestId),
121175
+ accountId: manifestId,
121176
+ totalSections: results.length,
121177
+ succeeded,
121178
+ failed,
121179
+ concurrency,
121180
+ elapsedMs: performance5.now() - overallT0,
121181
+ results
121182
+ };
121183
+ console.log(JSON.stringify(summary));
121184
+ if (failed > 0) process.exit(2);
121185
+ }
121186
+
121187
+ // src/commands/facebook-analysis/register-cli.ts
121188
+ function registerFacebookAnalysisCommands(program2) {
121189
+ const sectionHelp = FACEBOOK_SECTION_NAMES.join(", ");
121190
+ program2.command("facebook-analysis").description(
121191
+ "Facebook Ads \u8D26\u6237\u5206\u6790\u6279\u91CF\u62C9\u53D6\uFF08TSO reporting/media-account/FacebookAds/\u2026\uFF0C7 \u4E2A Section\uFF09"
121192
+ ).requiredOption(
121193
+ "-a, --account <id>",
121194
+ "Facebook \u5E7F\u544A\u8D26\u6237 mediaCustomerId\uFF08\u6570\u5B57\u6216 act_<\u6570\u5B57>\uFF09"
121195
+ ).requiredOption(
121196
+ "--json-out <dir>",
121197
+ "\u7ED3\u679C\u843D\u76D8\u76EE\u5F55\uFF1B\u6BCF\u7EF4\u5EA6 `<section>-<accountId>.json` + `report-manifest-<accountId>.json`"
121198
+ ).option("-t, --token <token>", "Auth Token").option(
121199
+ "--start <date>",
121200
+ "\u5F00\u59CB\u65E5\u671F YYYY-MM-DD\uFF08\u4E0E --end \u540C\u4F20\u6216\u540C\u7701\u7565\uFF1B\u7701\u7565=\u8FD1 7 \u5929\u622A\u81F3\u6628\u5929\uFF09"
121201
+ ).option("--end <date>", "\u7ED3\u675F\u65E5\u671F YYYY-MM-DD").option(
121202
+ "--sections <list>",
121203
+ `\u4EC5\u6267\u884C\u6307\u5B9A\u7EF4\u5EA6\uFF08\u9017\u53F7\u5206\u9694\uFF09\uFF0C\u5982 overview,ad-sets,platform\uFF1B\u7701\u7565=\u5168\u90E8 7 \u4E2A\u3002\u53EF\u9009\uFF1A${sectionHelp}\uFF1BGoogle \u522B\u540D\uFF1Acampaigns,geographic,devices,ads,materials`
121204
+ ).option("--exclude <list>", "\u6392\u9664\u6307\u5B9A\u7EF4\u5EA6\uFF08\u9017\u53F7\u5206\u9694\uFF09\uFF0C\u4E0E --sections \u53EF\u53E0\u52A0").option(
121205
+ "--limit <n>",
121206
+ "\u4EC5 country \u7EF4\u5EA6\uFF1A\u6309 spend \u964D\u5E8F\u53D6\u524D N \u6761\uFF08\u4E0D\u4F20\u5219\u8FD4\u56DE\u5168\u90E8\u56FD\u5BB6\uFF09",
121207
+ (v) => parseInt(v, 10)
121208
+ ).option("--concurrency <n>", "\u5E76\u53D1\u6570\uFF0C\u9ED8\u8BA4 5\uFF0C\u4E0A\u9650 16", (v) => parseInt(v, 10)).option("--verbose", "\u8BE6\u7EC6\u9519\u8BEF\u4FE1\u606F", false).action(async (opts) => {
121209
+ await runAllFacebookSections(opts);
121210
+ });
121211
+ }
121212
+
121213
+ // src/index.ts
120903
121214
  init_version();
120904
121215
  init_cli_json_snapshot();
120905
121216
  installProcessHandlers();
120906
121217
  function getVersion() {
120907
121218
  try {
120908
- const __dirname3 = path21.dirname(fileURLToPath4(import.meta.url));
120909
- const pkgPath = path21.join(__dirname3, "..", "package.json");
121219
+ const __dirname3 = path22.dirname(fileURLToPath4(import.meta.url));
121220
+ const pkgPath = path22.join(__dirname3, "..", "package.json");
120910
121221
  const pkg = JSON.parse(fs14.readFileSync(pkgPath, "utf8"));
120911
121222
  return pkg.version ?? "0.0.0";
120912
121223
  } catch {
@@ -120946,6 +121257,7 @@ var REGISTRARS = [
120946
121257
  register13,
120947
121258
  registerGoogleAnalysisCommands,
120948
121259
  registerGoogleAnalysisBatchCommands,
121260
+ registerFacebookAnalysisCommands,
120949
121261
  register14,
120950
121262
  register15,
120951
121263
  register16,