siluzan-tso-cli 1.1.47 → 1.1.48-beta.2

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 (107) hide show
  1. package/README.md +2 -1
  2. package/dist/index.js +1999 -42
  3. package/dist/skill/AGENTS.md +1 -0
  4. package/dist/skill/SKILL.md +12 -2
  5. package/dist/skill/_meta.json +2 -2
  6. package/dist/skill/assets/meta-lead-create-template.json +105 -0
  7. package/dist/skill/assets/meta-lead-create-template.md +46 -0
  8. package/dist/skill/references/README.md +12 -1
  9. package/dist/skill/references/accounts/accounts-list.md +1 -1
  10. package/dist/skill/references/accounts/accounts-permissions.md +2 -2
  11. package/dist/skill/references/accounts/finance.md +1 -1
  12. package/dist/skill/references/accounts/open-account-by-media.md +1 -1
  13. package/dist/skill/references/accounts/open-account-google-ui.md +1 -1
  14. package/dist/skill/references/analytics/rag.md +1 -1
  15. package/dist/skill/references/analytics/reporting.md +5 -5
  16. package/dist/skill/references/core/intent-routing.md +4 -0
  17. package/dist/skill/references/core/setup.md +5 -5
  18. package/dist/skill/references/core/workflows.md +22 -2
  19. package/dist/skill/references/meta-ads/meta-ads-read.md +33 -0
  20. package/dist/skill/references/meta-ads/meta-ads-write.md +70 -0
  21. package/dist/skill/references/meta-ads/meta-ads.md +37 -0
  22. package/dist/skill/references/meta-ads/meta-lead-launch-plan-template.md +168 -0
  23. package/dist/skill/references/misc/tso-home.md +2 -2
  24. package/dist/skill/references/operations/clue.md +2 -1
  25. package/dist/skill/scripts/install.ps1 +3 -3
  26. package/dist/skill/scripts/install.sh +3 -3
  27. package/eval/cases/accounts-entityid-vs-mediaccustomerid.scenario.json +14 -2
  28. package/eval/cases/accounts-mcc-bind-inquiry.scenario.json +3 -1
  29. package/eval/cases/accounts-single-balance-not-bulk.scenario.json +14 -3
  30. package/eval/cases/budget-display-not-raw-micros.scenario.json +8 -1
  31. package/eval/cases/clue-meta-leads-json.scenario.json +14 -2
  32. package/eval/cases/clue-tiktok-leads-json.scenario.json +11 -2
  33. package/eval/cases/destructive-account-delink-needs-confirm.scenario.json +9 -3
  34. package/eval/cases/destructive-forewarning-delete-needs-confirm.scenario.json +9 -3
  35. package/eval/cases/destructive-invoice-apply-needs-confirm.scenario.json +9 -3
  36. package/eval/cases/facebook-analysis-google-section-aliases.scenario.json +11 -2
  37. package/eval/cases/facebook-analysis-not-google-keywords.scenario.json +10 -3
  38. package/eval/cases/facebook-analysis-period-default.scenario.json +14 -2
  39. package/eval/cases/finance-invoice-info-list.scenario.json +11 -3
  40. package/eval/cases/forewarning-list-google.scenario.json +14 -3
  41. package/eval/cases/google-ads-no-structural-without-confirm.scenario.json +6 -2
  42. package/eval/cases/google-analysis-keywords-route.scenario.json +14 -2
  43. package/eval/cases/guard-budget-circuit-all-google.scenario.json +12 -2
  44. package/eval/cases/guard-zero-conv-empty-spend.scenario.json +12 -2
  45. package/eval/cases/human-p1-multiturn.scenario.json +5 -1
  46. package/eval/cases/ir-p1-vs-p8-account-vs-url.scenario.json +17 -5
  47. package/eval/cases/ir-p3-cpa-monitor-not-p7.scenario.json +19 -2
  48. package/eval/cases/ir-p3-zero-inquiry-not-p7.scenario.json +16 -3
  49. package/eval/cases/ir-p4-period-not-diagnosis.scenario.json +16 -3
  50. package/eval/cases/ir-p8-meets-google-ads-requirements-with-url.scenario.json +28 -3
  51. package/eval/cases/ir-p8-network-diagnosis-with-url.scenario.json +25 -3
  52. package/eval/cases/ir-p8-website-monitor-report-with-url.scenario.json +28 -3
  53. package/eval/cases/ir-p8-website-test-report-no-url.scenario.json +14 -3
  54. package/eval/cases/ir-p9-industry-not-p8.scenario.json +19 -3
  55. package/eval/cases/ir-vague-report-clarify.scenario.json +14 -3
  56. package/eval/cases/ir-w3-vs-p8-from-website.scenario.json +8 -2
  57. package/eval/cases/ir-w5-keyword-planner-url-core-terms.scenario.json +22 -2
  58. package/eval/cases/meta-single-balance-not-bulk.scenario.json +17 -3
  59. package/eval/cases/no-legacy-json-flag.scenario.json +6 -2
  60. package/eval/cases/open-account-google-noninteractive.scenario.json +4 -2
  61. package/eval/cases/open-account-tiktok-license-file.scenario.json +3 -1
  62. package/eval/cases/optimize-list-by-account.scenario.json +11 -3
  63. package/eval/cases/p1-single-account-profile.scenario.json +11 -1
  64. package/eval/cases/p2-balance-scan-bulk.scenario.json +9 -2
  65. package/eval/cases/p3-accounts-digest.scenario.json +5 -1
  66. package/eval/cases/p3-cpa-scan-max-cpa.scenario.json +14 -2
  67. package/eval/cases/p4-fb-meta-period-report.scenario.json +12 -2
  68. package/eval/cases/p4-period-report-window.scenario.json +8 -1
  69. package/eval/cases/pmax-asset-group-create-with-bg.scenario.json +13 -2
  70. package/eval/cases/pmax-brand-edit-routing.scenario.json +16 -3
  71. package/eval/cases/pmax-edit-not-campaign-edit.scenario.json +12 -2
  72. package/eval/cases/pmax-enable-brand-guidelines.scenario.json +12 -2
  73. package/eval/cases/pmax-no-assets-update-brand-fields.scenario.json +14 -3
  74. package/eval/cases/rag-before-keyword-expand.scenario.json +11 -1
  75. package/eval/cases/rag-list-then-query.scenario.json +14 -2
  76. package/eval/cases/report-list-google.scenario.json +11 -2
  77. package/eval/cases/report-push-list-google.scenario.json +11 -2
  78. package/eval/cases/setup-login-or-env.scenario.json +3 -1
  79. package/eval/cases/setup-siluzan-data-permission-env.scenario.json +3 -1
  80. package/eval/cases/tiktok-bc-bind-inquiry.scenario.json +6 -2
  81. package/eval/cases/time-range-user-delegates-default.scenario.json +8 -1
  82. package/eval/cases/tips-json-out-filtering.scenario.json +3 -1
  83. package/eval/cases/tips-large-json-pagination.scenario.json +3 -1
  84. package/eval/cases/uj-ad-campaign-validate-before-create-stub.scenario.json +11 -2
  85. package/eval/cases/uj-ad-outdoor-campgear-search-plan.scenario.json +3 -1
  86. package/eval/cases/uj-ad-website-search-plan-table-format.scenario.json +3 -1
  87. package/eval/cases/uj-analytics-30d-pdf-campaign-device-geo.scenario.json +18 -6
  88. package/eval/cases/uj-analytics-compare-google-tiktok-last-month-roi.scenario.json +8 -1
  89. package/eval/cases/uj-analytics-ecommerce-industry-report.scenario.json +14 -2
  90. package/eval/cases/uj-analytics-google-weekly-trends-campaigns-keywords.scenario.json +11 -2
  91. package/eval/cases/uj-analytics-report-push-weekly-email.scenario.json +3 -1
  92. package/eval/cases/uj-finance-invoice-records-this-month.scenario.json +11 -2
  93. package/eval/cases/uj-life-newbie-siluzan-google-end-to-end.scenario.json +4 -1
  94. package/eval/cases/uj-meta-ad-validate-before-create-stub.scenario.json +30 -0
  95. package/eval/cases/uj-ops-google-accounts-list-normal.scenario.json +17 -2
  96. package/eval/cases/uj-ops-google-yesterday-spend-conversions.scenario.json +14 -2
  97. package/eval/cases/uj-ops-open-google-b2c-usd-shenzhen.scenario.json +1 -1
  98. package/eval/cases/uj-ops-pause-worst-adgroup-confirm.scenario.json +6 -2
  99. package/eval/cases/uj-ops-tiktok-leads-last-week.scenario.json +17 -3
  100. package/eval/cases/uj-patrol-cpc-spike-adgroups-over-15.scenario.json +5 -1
  101. package/eval/cases/uj-patrol-forewarning-create-daily-cap-3000.scenario.json +3 -1
  102. package/eval/cases/uj-patrol-forewarning-trigger-records.scenario.json +17 -3
  103. package/eval/cases/uj-patrol-google-balances-low.scenario.json +11 -2
  104. package/eval/cases/uj-roi-optimize-records-then-execute-cautiously.scenario.json +14 -3
  105. package/eval/cases/uj-roi-search-terms-add-negative-keywords.scenario.json +14 -2
  106. package/eval/cases/yandex-stats-use-porg-mediaccustomerid.scenario.json +21 -3
  107. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -4026,7 +4026,7 @@ var DEFAULT_API_BASE;
4026
4026
  var init_defaults = __esm({
4027
4027
  "src/config/defaults.ts"() {
4028
4028
  "use strict";
4029
- DEFAULT_API_BASE = "https://tso-api.siluzan.com";
4029
+ DEFAULT_API_BASE = "https://tso-api-ci.siluzan.com";
4030
4030
  }
4031
4031
  });
4032
4032
 
@@ -5572,8 +5572,8 @@ var init_accounts_digest2 = __esm({
5572
5572
  async function withAsyncLock(key, fn) {
5573
5573
  const prev = chains.get(key) ?? Promise.resolve();
5574
5574
  let release;
5575
- const gate = new Promise((resolve23) => {
5576
- release = resolve23;
5575
+ const gate = new Promise((resolve24) => {
5576
+ release = resolve24;
5577
5577
  });
5578
5578
  chains.set(
5579
5579
  key,
@@ -6179,14 +6179,14 @@ function computeBackoffMs(attempt, policy) {
6179
6179
  return capped + jitter;
6180
6180
  }
6181
6181
  function sleep3(ms, signal) {
6182
- return new Promise((resolve23, reject) => {
6182
+ return new Promise((resolve24, reject) => {
6183
6183
  if (signal?.aborted) {
6184
6184
  reject(new Error("retry sleep aborted"));
6185
6185
  return;
6186
6186
  }
6187
6187
  const timer = setTimeout(() => {
6188
6188
  signal?.removeEventListener("abort", onAbort);
6189
- resolve23();
6189
+ resolve24();
6190
6190
  }, ms);
6191
6191
  const onAbort = () => {
6192
6192
  clearTimeout(timer);
@@ -102747,12 +102747,12 @@ import fs22 from "fs";
102747
102747
  import fsPromises2 from "fs/promises";
102748
102748
  import path27 from "path";
102749
102749
  import { fileURLToPath as fileURLToPath6 } from "url";
102750
- function resolveSkillTemplatePath3(basename11) {
102750
+ function resolveSkillTemplatePath3(basename13) {
102751
102751
  const dir = path27.dirname(fileURLToPath6(import.meta.url));
102752
102752
  const candidates = [
102753
- path27.join(dir, "skill", "report-templates", basename11),
102754
- path27.join(dir, "skill", "siluzan-ads", "report-templates", basename11),
102755
- path27.join(dir, "..", "..", "assets", "siluzan-ads", "report-templates", basename11)
102753
+ path27.join(dir, "skill", "report-templates", basename13),
102754
+ path27.join(dir, "skill", "siluzan-ads", "report-templates", basename13),
102755
+ path27.join(dir, "..", "..", "assets", "siluzan-ads", "report-templates", basename13)
102756
102756
  ];
102757
102757
  for (const p of candidates) {
102758
102758
  if (fs22.existsSync(p)) return p;
@@ -122320,6 +122320,1962 @@ function register21(program2) {
122320
122320
  );
122321
122321
  }
122322
122322
 
122323
+ // src/commands/meta-ad/account.ts
122324
+ init_cli_table();
122325
+
122326
+ // src/commands/meta-ad/client.ts
122327
+ init_auth();
122328
+
122329
+ // src/commands/meta-ad/_shared.ts
122330
+ init_cli_json_snapshot();
122331
+ var META_PREDEFINED_QUESTION_TYPES = [
122332
+ "EMAIL",
122333
+ "FULL_NAME",
122334
+ "FIRST_NAME",
122335
+ "LAST_NAME",
122336
+ "PHONE",
122337
+ "CITY",
122338
+ "STATE",
122339
+ "COUNTRY",
122340
+ "POST_CODE",
122341
+ "ZIP",
122342
+ "COMPANY_NAME",
122343
+ "JOB_TITLE",
122344
+ "WORK_EMAIL",
122345
+ "WORK_PHONE_NUMBER",
122346
+ "DATE_OF_BIRTH",
122347
+ "GENDER",
122348
+ "MARITAL_STATUS"
122349
+ ];
122350
+ var META_OPTIMIZATION_GOALS = ["LEAD_GENERATION", "QUALITY_LEAD"];
122351
+ var ZERO_DECIMAL_CURRENCIES = /* @__PURE__ */ new Set([
122352
+ "BIF",
122353
+ "CLP",
122354
+ "DJF",
122355
+ "GNF",
122356
+ "ISK",
122357
+ "JPY",
122358
+ "KMF",
122359
+ "KRW",
122360
+ "PYG",
122361
+ "RWF",
122362
+ "UGX",
122363
+ "VND",
122364
+ "VUV",
122365
+ "XAF",
122366
+ "XOF",
122367
+ "XPF"
122368
+ ]);
122369
+ var THREE_DECIMAL_CURRENCIES = /* @__PURE__ */ new Set(["BHD", "IQD", "JOD", "KWD", "LYD", "OMR", "TND"]);
122370
+ function currencyMinorExponent(currency) {
122371
+ const c = (currency ?? "").trim().toUpperCase();
122372
+ if (ZERO_DECIMAL_CURRENCIES.has(c)) return 0;
122373
+ if (THREE_DECIMAL_CURRENCIES.has(c)) return 3;
122374
+ return 2;
122375
+ }
122376
+ function toMinorUnitString(major, currency) {
122377
+ if (!Number.isFinite(major) || major < 0) {
122378
+ throw new Error(`\u91D1\u989D\u5FC5\u987B\u662F\u975E\u8D1F\u6709\u9650\u6570\u5B57\uFF0C\u6536\u5230 ${major}`);
122379
+ }
122380
+ const exp = currencyMinorExponent(currency);
122381
+ const n = Math.round(major * 10 ** exp);
122382
+ if (major > 0 && n < 1) {
122383
+ throw new Error(`\u91D1\u989D\u8FC7\u5C0F\uFF0C\u6309 ${currency || "USD"} \u65E0\u6CD5\u8868\u793A\u6700\u5C0F\u8D27\u5E01\u5355\u4F4D`);
122384
+ }
122385
+ return String(n);
122386
+ }
122387
+ function fromMinorUnitDisplay(raw, currency) {
122388
+ if (raw == null || raw === "") return null;
122389
+ const n = Number(raw);
122390
+ if (!Number.isFinite(n)) return null;
122391
+ return n / 10 ** currencyMinorExponent(currency);
122392
+ }
122393
+ var META_MONEY_KEYS = [
122394
+ "daily_budget",
122395
+ "lifetime_budget",
122396
+ "bid_amount",
122397
+ "spend_cap",
122398
+ "amount_spent",
122399
+ "balance"
122400
+ ];
122401
+ function withMoneyDisplay(obj, currency) {
122402
+ const out = { ...obj };
122403
+ for (const key of META_MONEY_KEYS) {
122404
+ if (key in obj) {
122405
+ out[`${key}Display`] = fromMinorUnitDisplay(obj[key], currency);
122406
+ }
122407
+ }
122408
+ return out;
122409
+ }
122410
+ function formatYuanDisplay(value) {
122411
+ return value == null ? "\u2014" : String(value);
122412
+ }
122413
+ var META_PENDING_ACCOUNT = "[PENDING_ACCOUNT]";
122414
+ var META_PENDING_PAGE = "[PENDING_PAGE]";
122415
+ function isPendingMetaAccount(raw) {
122416
+ const t = (raw ?? "").trim();
122417
+ return t === "" || t === META_PENDING_ACCOUNT || t === "PENDING_ACCOUNT";
122418
+ }
122419
+ function isPendingMetaPage(raw) {
122420
+ const t = (raw ?? "").trim();
122421
+ return t === "" || t === META_PENDING_PAGE || t === "PENDING_PAGE";
122422
+ }
122423
+ function parseMetaAccountId(raw) {
122424
+ const t = (raw ?? "").trim();
122425
+ if (isPendingMetaAccount(t)) {
122426
+ return { ok: false, error: "account \u4ECD\u662F\u5360\u4F4D\uFF0Ccreate \u524D\u987B\u6362\u6210\u771F\u5B9E mediaCustomerId" };
122427
+ }
122428
+ if (!t) return { ok: false, error: "account \u4E0D\u80FD\u4E3A\u7A7A" };
122429
+ const digits = t.startsWith("act_") ? t.slice(4) : t;
122430
+ if (!/^\d+$/.test(digits)) {
122431
+ return { ok: false, error: "account \u987B\u4E3A\u6570\u5B57 mediaCustomerId \u6216 act_<\u6570\u5B57>" };
122432
+ }
122433
+ return { ok: true, digits, apiId: `act_${digits}` };
122434
+ }
122435
+ function resolveMetaAccountId(raw) {
122436
+ const parsed = parseMetaAccountId(raw);
122437
+ if (!parsed.ok) {
122438
+ console.error(`
122439
+ \u274C ${parsed.error}
122440
+ `);
122441
+ process.exit(1);
122442
+ }
122443
+ return { digits: parsed.digits, apiId: parsed.apiId };
122444
+ }
122445
+ function requireFacebookApi(config) {
122446
+ const raw = (config.facebookApiUrl ?? "").replace(/\/$/, "");
122447
+ if (!raw) {
122448
+ console.error(
122449
+ "\n\u274C \u672A\u80FD\u63A8\u5BFC Facebook \u7F51\u5173\u5730\u5740\uFF08facebookApiUrl\uFF09\u3002\n \u4ECE TSO API \u5730\u5740\u81EA\u52A8\u63A8\u5BFC\uFF08tso-api[-ci].siluzan.com \u2192 facebookapi[-ci].mysiluzan.com\uFF09\u3002\n"
122450
+ );
122451
+ process.exit(1);
122452
+ }
122453
+ return raw;
122454
+ }
122455
+ function metaAdsBaseUrl(facebookApiUrl, account) {
122456
+ const { digits } = resolveMetaAccountId(account);
122457
+ return `${facebookApiUrl.replace(/\/$/, "")}/api/fb/ads/${digits}`;
122458
+ }
122459
+ function formatMetaAdError(err) {
122460
+ const raw = err instanceof Error ? err.message : String(err);
122461
+ const start = raw.indexOf("{");
122462
+ const end = raw.lastIndexOf("}");
122463
+ if (start >= 0 && end > start) {
122464
+ try {
122465
+ const parsed = JSON.parse(raw.slice(start, end + 1));
122466
+ const code = parsed.code != null ? String(parsed.code) : "";
122467
+ const message = typeof parsed.message === "string" && parsed.message || typeof parsed.Message === "string" && parsed.Message || "";
122468
+ if (code || message) {
122469
+ const prefix = code ? `[${code}] ` : "";
122470
+ return `${prefix}${message || raw}`;
122471
+ }
122472
+ } catch {
122473
+ }
122474
+ }
122475
+ return raw;
122476
+ }
122477
+ function isBidAmountRequired(err) {
122478
+ const text = formatMetaAdError(err);
122479
+ return /BID_AMOUNT_REQUIRED/i.test(text) || /缺少竞价金额/.test(text) || /必须提供 bid_amount/.test(text) || /必须在 bid_amount/.test(text);
122480
+ }
122481
+ function failMetaAd(action, err) {
122482
+ console.error(`
122483
+ \u274C ${action}\uFF1A${formatMetaAdError(err)}
122484
+ `);
122485
+ process.exit(1);
122486
+ }
122487
+ async function emitMetaJson(opts, section, commandLabel, payload, idSuffix) {
122488
+ return emitCliJsonOrSnapshot(opts, {
122489
+ section,
122490
+ commandLabel,
122491
+ commandHint: idSuffix,
122492
+ payload,
122493
+ idSuffix
122494
+ });
122495
+ }
122496
+ function isHttpUrl(value) {
122497
+ try {
122498
+ const u = new URL(value);
122499
+ return u.protocol === "http:" || u.protocol === "https:";
122500
+ } catch {
122501
+ return false;
122502
+ }
122503
+ }
122504
+ function isIsoCountry(code) {
122505
+ return /^[A-Za-z]{2}$/.test(code.trim());
122506
+ }
122507
+ function normalizeQuestionType(type) {
122508
+ return type.trim().toUpperCase();
122509
+ }
122510
+ function isPredefinedQuestionType(type) {
122511
+ return META_PREDEFINED_QUESTION_TYPES.includes(
122512
+ normalizeQuestionType(type)
122513
+ );
122514
+ }
122515
+ function extractCreatedId(data) {
122516
+ if (!data || typeof data !== "object") return void 0;
122517
+ const rec = data;
122518
+ const id = rec.id ?? rec.Id;
122519
+ if (id == null) return void 0;
122520
+ const s = String(id).trim();
122521
+ return s || void 0;
122522
+ }
122523
+ function extractImageHash(data) {
122524
+ if (!data || typeof data !== "object") return void 0;
122525
+ const rec = data;
122526
+ const hash = rec.hash ?? rec.image_hash;
122527
+ if (hash == null) return void 0;
122528
+ const s = String(hash).trim();
122529
+ return s || void 0;
122530
+ }
122531
+ function parseCommaList(raw) {
122532
+ if (!raw?.trim()) return [];
122533
+ return raw.split(",").map((s) => s.trim()).filter(Boolean);
122534
+ }
122535
+ function parseOptionalNumber(raw) {
122536
+ if (raw == null || raw === "") return void 0;
122537
+ const n = typeof raw === "number" ? raw : Number(raw);
122538
+ if (!Number.isFinite(n)) return void 0;
122539
+ return n;
122540
+ }
122541
+
122542
+ // src/commands/meta-ad/client.ts
122543
+ function metaAdContext(token) {
122544
+ const config = loadConfig(token);
122545
+ return { config, facebookApiUrl: requireFacebookApi(config) };
122546
+ }
122547
+ function adsUrl(facebookApiUrl, account, path47) {
122548
+ const base = metaAdsBaseUrl(facebookApiUrl, account);
122549
+ const p = path47.startsWith("/") ? path47 : `/${path47}`;
122550
+ return `${base}${p}`;
122551
+ }
122552
+ async function metaAdGet(url, config, verbose, action) {
122553
+ try {
122554
+ return await apiFetch2(url, config, {}, verbose);
122555
+ } catch (err) {
122556
+ failMetaAd(action, err);
122557
+ }
122558
+ }
122559
+ async function metaAdPost(url, config, body, verbose, action) {
122560
+ try {
122561
+ return await apiFetch2(url, config, { method: "POST", body: JSON.stringify(body) }, verbose);
122562
+ } catch (err) {
122563
+ failMetaAd(action, err);
122564
+ }
122565
+ }
122566
+ function readAccountCurrency(account) {
122567
+ const c = account?.currency;
122568
+ return typeof c === "string" && c.trim() ? c.trim() : "USD";
122569
+ }
122570
+ async function fetchAccountCurrency(facebookApiUrl, account, config, verbose) {
122571
+ const acc = await metaAdGet(
122572
+ adsUrl(facebookApiUrl, account, "/account"),
122573
+ config,
122574
+ verbose,
122575
+ "\u8BFB\u53D6\u8D26\u6237\u5E01\u79CD\u5931\u8D25"
122576
+ );
122577
+ return readAccountCurrency(acc);
122578
+ }
122579
+
122580
+ // src/commands/meta-ad/account.ts
122581
+ async function runMetaAdAccount(opts) {
122582
+ const { digits } = resolveMetaAccountId(opts.account);
122583
+ const { config, facebookApiUrl } = metaAdContext(opts.token);
122584
+ const raw = await metaAdGet(
122585
+ adsUrl(facebookApiUrl, digits, "/account"),
122586
+ config,
122587
+ opts.verbose,
122588
+ "\u8BFB\u53D6\u5E7F\u544A\u8D26\u6237\u5931\u8D25"
122589
+ );
122590
+ const currency = typeof raw.currency === "string" ? raw.currency : "USD";
122591
+ const payload = withMoneyDisplay(raw, currency);
122592
+ if (await emitMetaJson(opts, "meta-ad-account", "meta-ad account", payload, digits)) return;
122593
+ console.log(`
122594
+ Meta \u5E7F\u544A\u8D26\u6237 ${digits}\uFF08${currency}\uFF09
122595
+ `);
122596
+ printCliTable(
122597
+ [
122598
+ {
122599
+ id: String(payload.account_id ?? payload.id ?? digits),
122600
+ name: String(payload.name ?? "\u2014"),
122601
+ status: String(payload.account_status ?? "\u2014"),
122602
+ spent: formatYuanDisplay(payload.amount_spentDisplay),
122603
+ balance: formatYuanDisplay(payload.balanceDisplay),
122604
+ cap: formatYuanDisplay(payload.spend_capDisplay)
122605
+ }
122606
+ ],
122607
+ [
122608
+ { key: "id", header: "\u8D26\u6237ID" },
122609
+ { key: "name", header: "\u540D\u79F0" },
122610
+ { key: "status", header: "\u72B6\u6001" },
122611
+ { key: "spent", header: "\u5DF2\u82B1\u8D39(\u5143)" },
122612
+ { key: "balance", header: "\u4F59\u989D(\u5143)" },
122613
+ { key: "cap", header: "\u82B1\u8D39\u4E0A\u9650(\u5143)" }
122614
+ ]
122615
+ );
122616
+ console.log();
122617
+ }
122618
+
122619
+ // src/commands/meta-ad/create.ts
122620
+ init_auth();
122621
+ init_cli_json_snapshot();
122622
+ import { isAbsolute as isAbsolute7, resolve as resolve11, dirname as dirname12, basename as basename9 } from "path";
122623
+
122624
+ // src/commands/meta-ad/bodies.ts
122625
+ function buildCreateFormBody(form) {
122626
+ const questions = (form.questions ?? []).map((q) => {
122627
+ const type = normalizeQuestionType(q.type);
122628
+ const item = { type };
122629
+ if (type === "CUSTOM") {
122630
+ if (q.label) item.label = q.label;
122631
+ if (q.key) item.key = q.key;
122632
+ }
122633
+ return item;
122634
+ });
122635
+ const body = {
122636
+ name: form.name,
122637
+ locale: form.locale || "en_US",
122638
+ privacy_policy: { url: form.privacyPolicyUrl },
122639
+ questions
122640
+ };
122641
+ if (form.followUpActionUrl) body.follow_up_action_url = form.followUpActionUrl;
122642
+ if (form.thankYouPage) {
122643
+ const typ = form.thankYouPage;
122644
+ const thankYou = {};
122645
+ if (typ.title) thankYou.title = typ.title;
122646
+ if (typ.body) thankYou.body = typ.body;
122647
+ if (typ.buttonText) thankYou.button_text = typ.buttonText;
122648
+ if (typ.buttonType) thankYou.button_type = typ.buttonType;
122649
+ if (typ.websiteUrl) thankYou.website_url = typ.websiteUrl;
122650
+ if (Object.keys(thankYou).length > 0) body.thank_you_page = thankYou;
122651
+ }
122652
+ return body;
122653
+ }
122654
+ function buildUploadImageBody(opts) {
122655
+ const body = { name: opts.name };
122656
+ if (opts.bytesBase64) body.bytes = opts.bytesBase64;
122657
+ else if (opts.imageUrl) body.image_url = opts.imageUrl;
122658
+ return body;
122659
+ }
122660
+ function buildCreateCampaignBody(opts) {
122661
+ const c = opts.campaign;
122662
+ const body = {
122663
+ name: c.name,
122664
+ special_ad_categories: c.specialAdCategories ?? [],
122665
+ buying_type: c.buyingType || "AUCTION"
122666
+ };
122667
+ if (opts.dailyBudgetMinor) body.daily_budget = opts.dailyBudgetMinor;
122668
+ if (opts.lifetimeBudgetMinor) body.lifetime_budget = opts.lifetimeBudgetMinor;
122669
+ if (opts.spendCapMinor) body.spend_cap = opts.spendCapMinor;
122670
+ if (c.bidStrategy) body.bid_strategy = c.bidStrategy;
122671
+ if (c.startTime) body.start_time = c.startTime;
122672
+ if (c.stopTime) body.stop_time = c.stopTime;
122673
+ if (c.status && (c.status === "ACTIVE" || c.status === "PAUSED")) {
122674
+ body.status = c.status;
122675
+ }
122676
+ return body;
122677
+ }
122678
+ function buildCreateAdSetBody(opts) {
122679
+ const a = opts.adset;
122680
+ const targeting = {
122681
+ geo_locations: { countries: (a.countries ?? []).map((c) => c.trim().toUpperCase()) }
122682
+ };
122683
+ if (a.ageMin != null) targeting.age_min = a.ageMin;
122684
+ if (a.ageMax != null) targeting.age_max = a.ageMax;
122685
+ if (a.genders && a.genders.length > 0) targeting.genders = a.genders;
122686
+ if (a.flexibleSpec) targeting.flexible_spec = a.flexibleSpec;
122687
+ const body = {
122688
+ name: a.name,
122689
+ campaign_id: opts.campaignId,
122690
+ promoted_object: { page_id: opts.pageId },
122691
+ targeting,
122692
+ optimization_goal: a.optimizationGoal || "LEAD_GENERATION"
122693
+ };
122694
+ if (opts.dailyBudgetMinor) body.daily_budget = opts.dailyBudgetMinor;
122695
+ if (opts.lifetimeBudgetMinor) body.lifetime_budget = opts.lifetimeBudgetMinor;
122696
+ if (a.bidStrategy) body.bid_strategy = a.bidStrategy;
122697
+ if (opts.bidAmountMinor) body.bid_amount = opts.bidAmountMinor;
122698
+ if (a.startTime) body.start_time = a.startTime;
122699
+ if (a.endTime) body.end_time = a.endTime;
122700
+ if (a.status && (a.status === "ACTIVE" || a.status === "PAUSED")) {
122701
+ body.status = a.status;
122702
+ }
122703
+ return body;
122704
+ }
122705
+ function buildCreateCreativeBody(opts) {
122706
+ const c = opts.creative;
122707
+ return {
122708
+ name: c.name,
122709
+ object_story_spec: {
122710
+ page_id: opts.pageId,
122711
+ link_data: {
122712
+ message: c.message,
122713
+ link: c.link,
122714
+ image_hash: opts.imageHash,
122715
+ name: c.headline || c.name,
122716
+ call_to_action: {
122717
+ type: c.ctaType || "SIGN_UP",
122718
+ value: { lead_gen_form_id: opts.formId }
122719
+ }
122720
+ }
122721
+ }
122722
+ };
122723
+ }
122724
+ function buildCreateAdBody(opts) {
122725
+ const body = {
122726
+ name: opts.name,
122727
+ adset_id: opts.adsetId,
122728
+ creative: { creative_id: opts.creativeId }
122729
+ };
122730
+ if (opts.status && (opts.status === "ACTIVE" || opts.status === "PAUSED")) {
122731
+ body.status = opts.status;
122732
+ }
122733
+ return body;
122734
+ }
122735
+ function buildUpdateCampaignBody(opts) {
122736
+ const body = {};
122737
+ if (opts.name != null) body.name = opts.name;
122738
+ if (opts.dailyBudgetMinor != null) body.daily_budget = opts.dailyBudgetMinor;
122739
+ if (opts.lifetimeBudgetMinor != null) body.lifetime_budget = opts.lifetimeBudgetMinor;
122740
+ if (opts.bidStrategy != null) body.bid_strategy = opts.bidStrategy;
122741
+ if (opts.spendCapMinor != null) body.spend_cap = opts.spendCapMinor;
122742
+ if (opts.startTime != null) body.start_time = opts.startTime;
122743
+ if (opts.stopTime != null) body.stop_time = opts.stopTime;
122744
+ if (opts.specialAdCategories != null) body.special_ad_categories = opts.specialAdCategories;
122745
+ if (opts.status != null) body.status = opts.status;
122746
+ return body;
122747
+ }
122748
+ function buildUpdateAdSetBody(opts) {
122749
+ const body = {};
122750
+ if (opts.name != null) body.name = opts.name;
122751
+ if (opts.dailyBudgetMinor != null) body.daily_budget = opts.dailyBudgetMinor;
122752
+ if (opts.lifetimeBudgetMinor != null) body.lifetime_budget = opts.lifetimeBudgetMinor;
122753
+ if (opts.optimizationGoal != null) body.optimization_goal = opts.optimizationGoal;
122754
+ if (opts.bidStrategy != null) body.bid_strategy = opts.bidStrategy;
122755
+ if (opts.bidAmountMinor != null) body.bid_amount = opts.bidAmountMinor;
122756
+ if (opts.startTime != null) body.start_time = opts.startTime;
122757
+ if (opts.endTime != null) body.end_time = opts.endTime;
122758
+ if (opts.status != null) body.status = opts.status;
122759
+ const hasTargeting = opts.countries != null || opts.ageMin != null || opts.ageMax != null || opts.genders != null || opts.flexibleSpec != null;
122760
+ if (hasTargeting) {
122761
+ const targeting = {};
122762
+ if (opts.countries) {
122763
+ targeting.geo_locations = { countries: opts.countries.map((c) => c.trim().toUpperCase()) };
122764
+ }
122765
+ if (opts.ageMin != null) targeting.age_min = opts.ageMin;
122766
+ if (opts.ageMax != null) targeting.age_max = opts.ageMax;
122767
+ if (opts.genders) targeting.genders = opts.genders;
122768
+ if (opts.flexibleSpec) targeting.flexible_spec = opts.flexibleSpec;
122769
+ body.targeting = targeting;
122770
+ }
122771
+ return body;
122772
+ }
122773
+ function buildUpdateAdBody(opts) {
122774
+ const body = {};
122775
+ if (opts.name != null) body.name = opts.name;
122776
+ if (opts.creativeId != null) body.creative = { creative_id: opts.creativeId };
122777
+ if (opts.status != null) body.status = opts.status;
122778
+ return body;
122779
+ }
122780
+ function buildStatusBody(status) {
122781
+ return { status: status.trim() };
122782
+ }
122783
+ function formHasPredefinedQuestion(form) {
122784
+ return (form.questions ?? []).some((q) => isPredefinedQuestionType(q.type));
122785
+ }
122786
+
122787
+ // src/commands/meta-ad/load.ts
122788
+ import { readFileSync as readFileSync15 } from "fs";
122789
+ function stripMetaKeys2(value) {
122790
+ if (Array.isArray(value)) {
122791
+ return value.map((item) => stripMetaKeys2(item));
122792
+ }
122793
+ if (value && typeof value === "object") {
122794
+ const out = {};
122795
+ for (const [key, val] of Object.entries(value)) {
122796
+ if (key.startsWith("_")) continue;
122797
+ out[key] = stripMetaKeys2(val);
122798
+ }
122799
+ return out;
122800
+ }
122801
+ return value;
122802
+ }
122803
+ function tryLoadJsonFile(configFile) {
122804
+ try {
122805
+ return JSON.parse(readFileSync15(configFile, "utf8"));
122806
+ } catch {
122807
+ return null;
122808
+ }
122809
+ }
122810
+ function loadMetaLeadCreateConfig(configFile) {
122811
+ const raw = tryLoadJsonFile(configFile);
122812
+ if (!raw || typeof raw !== "object") {
122813
+ console.error(`
122814
+ \u274C \u8BFB\u53D6\u914D\u7F6E\u6587\u4EF6\u5931\u8D25\uFF08${configFile}\uFF09
122815
+ `);
122816
+ process.exit(1);
122817
+ }
122818
+ return stripMetaKeys2(raw);
122819
+ }
122820
+ function loadMetaLeadFormConfig(configFile) {
122821
+ const raw = tryLoadJsonFile(configFile);
122822
+ if (!raw || typeof raw !== "object") {
122823
+ console.error(`
122824
+ \u274C \u8BFB\u53D6\u8868\u5355\u914D\u7F6E\u5931\u8D25\uFF08${configFile}\uFF09
122825
+ `);
122826
+ process.exit(1);
122827
+ }
122828
+ const stripped = stripMetaKeys2(raw);
122829
+ if (stripped.form && typeof stripped.form === "object") {
122830
+ return stripped.form;
122831
+ }
122832
+ return stripped;
122833
+ }
122834
+
122835
+ // src/commands/meta-ad/images.ts
122836
+ import { readFileSync as readFileSync16 } from "fs";
122837
+ import { basename as basename8 } from "path";
122838
+ function readImageBytesBase64(imagePath) {
122839
+ const buf = readFileSync16(imagePath);
122840
+ if (buf.length < 1 || buf.length > 8 * 1024 * 1024) {
122841
+ throw new Error(`\u56FE\u7247\u5927\u5C0F\u987B\u4E3A 1 \u5B57\u8282\uFF5E8MB\uFF0C\u5F53\u524D ${buf.length} \u5B57\u8282`);
122842
+ }
122843
+ return buf.toString("base64");
122844
+ }
122845
+ async function runMetaAdImageUpload(opts) {
122846
+ const { digits } = resolveMetaAccountId(opts.account);
122847
+ const hasUrl = Boolean(opts.imageUrl?.trim());
122848
+ const hasPath = Boolean(opts.imagePath?.trim());
122849
+ if (hasUrl === hasPath) {
122850
+ console.error("\n\u274C --image-url \u4E0E --image-path \u987B\u4E8C\u9009\u4E00\n");
122851
+ process.exit(1);
122852
+ }
122853
+ if (hasUrl && !isHttpUrl(opts.imageUrl.trim())) {
122854
+ console.error("\n\u274C --image-url \u987B\u4E3A\u516C\u7F51 http/https\n");
122855
+ process.exit(1);
122856
+ }
122857
+ let bytes;
122858
+ let name2 = opts.name?.trim();
122859
+ if (hasPath) {
122860
+ try {
122861
+ bytes = readImageBytesBase64(opts.imagePath);
122862
+ } catch (err) {
122863
+ console.error(`
122864
+ \u274C \u8BFB\u53D6\u672C\u5730\u56FE\u7247\u5931\u8D25\uFF1A${err instanceof Error ? err.message : String(err)}
122865
+ `);
122866
+ process.exit(1);
122867
+ }
122868
+ name2 = name2 || basename8(opts.imagePath);
122869
+ }
122870
+ if (!name2) {
122871
+ console.error("\n\u274C --name \u5FC5\u586B\uFF08\u6216\u4ECE --image-path \u63A8\u65AD\uFF09\n");
122872
+ process.exit(1);
122873
+ }
122874
+ const { config, facebookApiUrl } = metaAdContext(opts.token);
122875
+ const body = buildUploadImageBody({
122876
+ name: name2,
122877
+ imageUrl: hasUrl ? opts.imageUrl.trim() : void 0,
122878
+ bytesBase64: bytes
122879
+ });
122880
+ const data = await metaAdPost(
122881
+ adsUrl(facebookApiUrl, digits, "/images"),
122882
+ config,
122883
+ body,
122884
+ opts.verbose,
122885
+ "\u4E0A\u4F20\u5E7F\u544A\u56FE\u5931\u8D25"
122886
+ );
122887
+ const hash = extractImageHash(data);
122888
+ const payload = { ...data, hash, name: data.name ?? name2 };
122889
+ if (await emitMetaJson(opts, "meta-ad-image-upload", "meta-ad image-upload", payload, digits)) {
122890
+ return;
122891
+ }
122892
+ console.log(`
122893
+ \u2705 \u56FE\u7247\u5DF2\u4E0A\u4F20 hash=${hash ?? "\u2014"}
122894
+ `);
122895
+ }
122896
+
122897
+ // src/commands/meta-ad/validate.ts
122898
+ init_cli_json_snapshot();
122899
+ function hasBudget(daily, lifetime) {
122900
+ return daily != null || lifetime != null;
122901
+ }
122902
+ function pushBudgetPairErrors(errors, label, daily, lifetime) {
122903
+ if (daily != null && lifetime != null) {
122904
+ errors.push(`${label} \u4E0D\u80FD\u540C\u65F6\u586B dailyBudget \u4E0E lifetimeBudget`);
122905
+ }
122906
+ if (daily != null && !(Number.isFinite(daily) && daily > 0)) {
122907
+ errors.push(`${label}.dailyBudget \u987B\u4E3A\u5927\u4E8E 0 \u7684\u6570\u5B57\uFF08\u4E3B\u5E01\u79CD\u5143\uFF09`);
122908
+ }
122909
+ if (lifetime != null && !(Number.isFinite(lifetime) && lifetime > 0)) {
122910
+ errors.push(`${label}.lifetimeBudget \u987B\u4E3A\u5927\u4E8E 0 \u7684\u6570\u5B57\uFF08\u4E3B\u5E01\u79CD\u5143\uFF09`);
122911
+ }
122912
+ }
122913
+ function runMetaLeadValidation(cfg) {
122914
+ const errors = [];
122915
+ const warnings = [];
122916
+ const pendingAccount = isPendingMetaAccount(cfg.account);
122917
+ if (pendingAccount) {
122918
+ warnings.push(
122919
+ "account \u4E3A\u5360\u4F4D\uFF08\u4EC5\u51FA\u65B9\u6848\uFF09\uFF1Bcreate \u524D\u987B\u6362\u6210\u771F\u5B9E mediaCustomerId\uFF0C\u5E76\u5148 meta-ad pages"
122920
+ );
122921
+ } else {
122922
+ const account = parseMetaAccountId(cfg.account);
122923
+ if (!account.ok) errors.push(account.error);
122924
+ }
122925
+ if (isPendingMetaPage(cfg.pageId)) {
122926
+ if (pendingAccount) {
122927
+ warnings.push("pageId \u4E3A\u5360\u4F4D/\u7A7A\uFF08\u4EC5\u51FA\u65B9\u6848\uFF09\uFF1Bcreate \u524D\u5148 meta-ad pages \u53D6\u53EF\u6295\u653E\u4E3B\u9875");
122928
+ } else {
122929
+ errors.push("pageId \u5FC5\u586B\uFF08\u5148 meta-ad pages \u53D6\u53EF\u6295\u653E\u4E3B\u9875\uFF09");
122930
+ }
122931
+ }
122932
+ const mode = String(cfg.budgetMode ?? "").trim().toUpperCase();
122933
+ if (mode !== "CBO" && mode !== "ABO") {
122934
+ errors.push("budgetMode \u987B\u4E3A CBO \u6216 ABO");
122935
+ }
122936
+ const campaign = cfg.campaign ?? {};
122937
+ const adset = cfg.adset ?? {};
122938
+ const form = cfg.form ?? {};
122939
+ const creative = cfg.creative ?? {};
122940
+ const ad = cfg.ad ?? {};
122941
+ if (!campaign.name?.trim()) errors.push("campaign.name \u5FC5\u586B");
122942
+ if (!adset.name?.trim()) errors.push("adset.name \u5FC5\u586B");
122943
+ if (!creative.name?.trim()) errors.push("creative.name \u5FC5\u586B");
122944
+ if (!ad.name?.trim()) errors.push("ad.name \u5FC5\u586B");
122945
+ pushBudgetPairErrors(errors, "campaign", campaign.dailyBudget, campaign.lifetimeBudget);
122946
+ pushBudgetPairErrors(errors, "adset", adset.dailyBudget, adset.lifetimeBudget);
122947
+ const campaignHasBudget = hasBudget(campaign.dailyBudget, campaign.lifetimeBudget);
122948
+ const adsetHasBudget = hasBudget(adset.dailyBudget, adset.lifetimeBudget);
122949
+ if (mode === "CBO") {
122950
+ if (!campaignHasBudget) errors.push("CBO \u987B\u5728 campaign \u4E0A\u4F20 dailyBudget \u6216 lifetimeBudget");
122951
+ if (adsetHasBudget)
122952
+ errors.push("CBO \u7EC4\u4E0A\u4E0D\u80FD\u4F20 dailyBudget / lifetimeBudget\uFF08CBO_ABO_CONFLICT\uFF09");
122953
+ }
122954
+ if (mode === "ABO") {
122955
+ if (!adsetHasBudget) errors.push("ABO \u987B\u5728 adset \u4E0A\u4F20 dailyBudget \u6216 lifetimeBudget");
122956
+ if (campaignHasBudget)
122957
+ errors.push("ABO \u7CFB\u5217\u4E0A\u4E0D\u80FD\u4F20 dailyBudget / lifetimeBudget\uFF08CBO_ABO_CONFLICT\uFF09");
122958
+ }
122959
+ const countries = adset.countries ?? [];
122960
+ if (countries.length === 0) {
122961
+ errors.push("adset.countries \u81F3\u5C11\u4E00\u4E2A\u56FD\u5BB6\uFF08ISO \u4E24\u4F4D\uFF0C\u5982 US\uFF09");
122962
+ } else {
122963
+ for (const c of countries) {
122964
+ if (!isIsoCountry(String(c))) errors.push(`adset.countries \u975E\u6CD5\uFF1A${c}\uFF08\u987B ISO \u4E24\u4F4D\uFF09`);
122965
+ }
122966
+ }
122967
+ if (adset.ageMin != null && (!Number.isFinite(adset.ageMin) || adset.ageMin < 13)) {
122968
+ errors.push("adset.ageMin \u987B \u2265 13");
122969
+ }
122970
+ if (adset.ageMax != null && (!Number.isFinite(adset.ageMax) || adset.ageMax > 65)) {
122971
+ errors.push("adset.ageMax \u987B \u2264 65");
122972
+ }
122973
+ if (adset.ageMin != null && adset.ageMax != null && Number.isFinite(adset.ageMin) && Number.isFinite(adset.ageMax) && adset.ageMin > adset.ageMax) {
122974
+ errors.push("adset.ageMin \u4E0D\u80FD\u5927\u4E8E ageMax");
122975
+ }
122976
+ if (adset.optimizationGoal) {
122977
+ const g = adset.optimizationGoal.trim().toUpperCase();
122978
+ if (!META_OPTIMIZATION_GOALS.includes(g)) {
122979
+ errors.push("adset.optimizationGoal \u53EA\u5141\u8BB8 LEAD_GENERATION / QUALITY_LEAD");
122980
+ }
122981
+ }
122982
+ if (adset.bidAmount != null && !(Number.isFinite(adset.bidAmount) && adset.bidAmount > 0)) {
122983
+ errors.push("adset.bidAmount \u987B\u4E3A\u5927\u4E8E 0 \u7684\u6570\u5B57\uFF08\u4E3B\u5E01\u79CD\u5143\uFF09");
122984
+ }
122985
+ const reuseId = form.reuseId?.trim();
122986
+ if (reuseId) {
122987
+ if (form.name || form.questions?.length || form.privacyPolicyUrl) {
122988
+ warnings.push("form.reuseId \u5DF2\u8BBE\uFF0C\u5C06\u5FFD\u7565\u65B0\u5EFA\u8868\u5355\u5B57\u6BB5");
122989
+ }
122990
+ } else {
122991
+ if (!form.name?.trim()) errors.push("form.name \u5FC5\u586B\uFF08\u6216\u6539\u586B form.reuseId\uFF09");
122992
+ if (!form.privacyPolicyUrl?.trim()) {
122993
+ errors.push("form.privacyPolicyUrl \u5FC5\u586B");
122994
+ } else if (!isHttpUrl(form.privacyPolicyUrl.trim())) {
122995
+ errors.push("form.privacyPolicyUrl \u987B\u4E3A http/https \u7EDD\u5BF9\u5730\u5740");
122996
+ }
122997
+ if (!form.questions?.length) {
122998
+ errors.push("form.questions \u81F3\u5C11\u4E00\u9879");
122999
+ } else {
123000
+ if (!formHasPredefinedQuestion(form)) {
123001
+ errors.push("form.questions \u81F3\u5C11\u5305\u542B\u4E00\u4E2A\u9884\u5B9A\u4E49\u5B57\u6BB5\uFF08\u5982 EMAIL / FULL_NAME / PHONE\uFF09");
123002
+ }
123003
+ for (const [i, q] of form.questions.entries()) {
123004
+ const t = normalizeQuestionType(q.type ?? "");
123005
+ if (!t) {
123006
+ errors.push(`form.questions[${i}].type \u5FC5\u586B`);
123007
+ continue;
123008
+ }
123009
+ if (t === "CUSTOM") {
123010
+ if (!q.label?.trim()) errors.push(`form.questions[${i}] CUSTOM \u5FC5\u987B\u5E26 label`);
123011
+ } else if (!isPredefinedQuestionType(t)) {
123012
+ errors.push(`form.questions[${i}].type \u4E0D\u652F\u6301\uFF1A${q.type}`);
123013
+ }
123014
+ }
123015
+ }
123016
+ }
123017
+ if (!creative.message?.trim()) errors.push("creative.message \u5FC5\u586B");
123018
+ if (!creative.link?.trim()) {
123019
+ errors.push("creative.link \u5FC5\u586B");
123020
+ } else if (!isHttpUrl(creative.link.trim())) {
123021
+ errors.push("creative.link \u987B\u4E3A http/https \u7EDD\u5BF9\u5730\u5740");
123022
+ }
123023
+ const imageSources = [creative.imageHash, creative.imageUrl, creative.imagePath].filter(
123024
+ (x) => Boolean(x?.trim())
123025
+ );
123026
+ if (imageSources.length === 0) {
123027
+ errors.push("creative \u987B\u63D0\u4F9B imageHash / imageUrl / imagePath \u4E4B\u4E00");
123028
+ }
123029
+ if (imageSources.length > 1) {
123030
+ warnings.push(
123031
+ "creative \u540C\u65F6\u7ED9\u4E86\u591A\u79CD\u56FE\u7247\u6765\u6E90\uFF0C\u4F18\u5148 imageHash\uFF0C\u5176\u6B21\u672C\u5730 imagePath\uFF0C\u518D\u6B21 imageUrl"
123032
+ );
123033
+ }
123034
+ if (creative.imageUrl && !isHttpUrl(creative.imageUrl.trim())) {
123035
+ errors.push("creative.imageUrl \u987B\u4E3A\u516C\u7F51 http/https");
123036
+ }
123037
+ if (cfg.activate === true) {
123038
+ warnings.push(
123039
+ "activate \u4E0D\u4F1A\u81EA\u52A8\u628A\u5BF9\u8C61\u6539\u6210 ACTIVE\uFF1B\u6295\u653E\u8BF7\u7528 meta-ad campaign-status / adset-status / ad-status"
123040
+ );
123041
+ }
123042
+ const createStatuses = ["ACTIVE", "PAUSED", void 0, ""];
123043
+ for (const [label, status] of [
123044
+ ["campaign.status", campaign.status],
123045
+ ["adset.status", adset.status],
123046
+ ["ad.status", ad.status]
123047
+ ]) {
123048
+ if (status && !createStatuses.includes(status.trim().toUpperCase())) {
123049
+ errors.push(`${label} \u521B\u5EFA\u65F6\u53EA\u5141\u8BB8 ACTIVE / PAUSED\uFF08\u7F3A\u7701 PAUSED\uFF09`);
123050
+ }
123051
+ if (status && status.trim().toUpperCase() === "ACTIVE") {
123052
+ warnings.push(`${label}=ACTIVE \u4F1A\u5728\u521B\u5EFA\u65F6\u5C1D\u8BD5\u6295\u653E\uFF1B\u5EFA\u8BAE\u7701\u7565\uFF0C\u9ED8\u8BA4 PAUSED\uFF0C\u786E\u8BA4\u540E\u518D\u6539\u72B6\u6001`);
123053
+ }
123054
+ }
123055
+ const plan = cfg.plan;
123056
+ if (plan) {
123057
+ const tracks = plan.tracks ?? [];
123058
+ if (tracks.length < 3) {
123059
+ warnings.push("plan.tracks \u5EFA\u8BAE\u81F3\u5C11 3 \u4E2A\u89C6\u89C9\u5DEE\u5F02\u5957\u7CFB\uFF08\u5BA1\u67E5\u7A3F A/B/C\uFF09");
123060
+ }
123061
+ const visual = plan.matrix?.visual ?? [];
123062
+ const bodyTone = plan.matrix?.bodyTone ?? [];
123063
+ const headline = plan.matrix?.headline ?? [];
123064
+ if (visual.length < 3 || bodyTone.length < 2 || headline.length < 2) {
123065
+ warnings.push("plan.matrix \u5EFA\u8BAE\u81F3\u5C11 3 \u89C6\u89C9 \xD7 2 \u6B63\u6587\u6C14\u8D28 \xD7 2 \u6807\u9898\uFF083\xD72\xD72\uFF09");
123066
+ }
123067
+ if ((plan.adSets?.length ?? 0) > 1) {
123068
+ warnings.push(
123069
+ "plan.adSets \u591A\u7EC4\u53EA\u5199\u5165\u5BA1\u67E5\u7A3F\uFF1B\u4E00\u6B21 meta-ad create \u53EA\u843D\u5730 JSON.adset\uFF0C\u5176\u4F59\u7528 adset-create \u7EED\u5EFA"
123070
+ );
123071
+ }
123072
+ } else {
123073
+ warnings.push(
123074
+ "\u7F3A\u5C11 plan\uFF1A\u521B\u5EFA\u524D\u987B\u6309 meta-lead-launch-plan-template.md \u4ECE JSON \u6295\u5F71\u5B8C\u6574\u5BA1\u67E5\u7A3F\uFF1B\u5EFA\u8BAE\u8865 plan \u4EE5\u514D\u5BA1\u67E5\u7A3F\u7F3A\u54C1\u724C/\u5957\u7CFB/\u77E9\u9635"
123075
+ );
123076
+ }
123077
+ return { errors, warnings };
123078
+ }
123079
+ async function runMetaAdValidate(opts) {
123080
+ const cfg = loadMetaLeadCreateConfig(opts.configFile);
123081
+ const { errors, warnings } = runMetaLeadValidation(cfg);
123082
+ const payload = {
123083
+ ok: errors.length === 0,
123084
+ configFile: opts.configFile,
123085
+ account: cfg.account?.toString().trim() || void 0,
123086
+ pageId: cfg.pageId?.toString().trim() || void 0,
123087
+ budgetMode: cfg.budgetMode,
123088
+ errors,
123089
+ warnings
123090
+ };
123091
+ if (await emitCliJsonOrSnapshot(opts, {
123092
+ section: "meta-ad-validate",
123093
+ commandLabel: "meta-ad validate",
123094
+ commandHint: opts.configFile,
123095
+ payload,
123096
+ idSuffix: payload.account
123097
+ })) {
123098
+ if (!payload.ok) process.exit(1);
123099
+ return;
123100
+ }
123101
+ if (warnings.length > 0) {
123102
+ console.warn("\n\u26A0\uFE0F Meta Lead \u914D\u7F6E\u8B66\u544A\uFF1A");
123103
+ for (const w of warnings) console.warn(` \u2022 ${w}`);
123104
+ }
123105
+ if (errors.length > 0) {
123106
+ console.error("\n\u274C Meta Lead \u914D\u7F6E\u6821\u9A8C\u5931\u8D25\uFF1A");
123107
+ for (const e of errors) console.error(` \u2022 ${e}`);
123108
+ console.error();
123109
+ process.exit(1);
123110
+ }
123111
+ console.log("\n\u2705 Meta Lead \u914D\u7F6E\u6821\u9A8C\u901A\u8FC7\uFF08\u672C\u5730\uFF0C\u672A\u8C03\u7F51\u5173\uFF09\n");
123112
+ }
123113
+
123114
+ // src/commands/meta-ad/create.ts
123115
+ function resolveBesideConfig(configFile, p) {
123116
+ if (isAbsolute7(p)) return p;
123117
+ return resolve11(dirname12(resolve11(configFile)), p);
123118
+ }
123119
+ async function postJson(url, config, body, verbose) {
123120
+ return apiFetch2(url, config, { method: "POST", body: JSON.stringify(body) }, verbose);
123121
+ }
123122
+ function resolveCreateImageSource(cfg, configFile) {
123123
+ const c = cfg.creative;
123124
+ const name2 = c.name?.trim() || "lead.jpg";
123125
+ if (c.imageHash?.trim()) return { imageHash: c.imageHash.trim(), name: name2 };
123126
+ if (c.imagePath?.trim()) {
123127
+ return {
123128
+ imagePath: resolveBesideConfig(configFile, c.imagePath.trim()),
123129
+ name: basename9(c.imagePath)
123130
+ };
123131
+ }
123132
+ return { imageUrl: c.imageUrl?.trim(), name: name2 };
123133
+ }
123134
+ async function runMetaAdCreate(opts) {
123135
+ const cfg = loadMetaLeadCreateConfig(opts.configFile);
123136
+ const { errors, warnings } = runMetaLeadValidation(cfg);
123137
+ if (warnings.length > 0) {
123138
+ console.warn("\n\u26A0\uFE0F Meta Lead \u914D\u7F6E\u8B66\u544A\uFF1A");
123139
+ for (const w of warnings) console.warn(` \u2022 ${w}`);
123140
+ }
123141
+ if (errors.length > 0) {
123142
+ console.error("\n\u274C Meta Lead \u914D\u7F6E\u6821\u9A8C\u5931\u8D25\uFF1A");
123143
+ for (const e of errors) console.error(` \u2022 ${e}`);
123144
+ console.error();
123145
+ process.exit(1);
123146
+ }
123147
+ const { digits } = resolveMetaAccountId(cfg.account);
123148
+ const config = loadConfig(opts.token);
123149
+ const facebookApiUrl = requireFacebookApi(config);
123150
+ const pageId = cfg.pageId.trim();
123151
+ const progress = { ok: false, account: digits, pageId };
123152
+ const emitProgress = async () => emitCliJsonOrSnapshot(opts, {
123153
+ section: "meta-ad-create",
123154
+ commandLabel: "meta-ad create",
123155
+ commandHint: opts.configFile,
123156
+ payload: progress,
123157
+ idSuffix: digits
123158
+ });
123159
+ try {
123160
+ const account = await apiFetch2(
123161
+ adsUrl(facebookApiUrl, digits, "/account"),
123162
+ config,
123163
+ {},
123164
+ opts.verbose
123165
+ );
123166
+ progress.currency = readAccountCurrency(account);
123167
+ } catch (err) {
123168
+ progress.failedStep = "account";
123169
+ progress.error = formatMetaAdError(err);
123170
+ await emitProgress();
123171
+ console.error(`
123172
+ \u274C \u8BFB\u53D6\u8D26\u6237\u5931\u8D25\uFF1A${progress.error}
123173
+ `);
123174
+ process.exit(1);
123175
+ }
123176
+ const currency = progress.currency ?? "USD";
123177
+ try {
123178
+ if (cfg.form.reuseId?.trim()) {
123179
+ progress.formId = cfg.form.reuseId.trim();
123180
+ } else {
123181
+ const data = await postJson(
123182
+ adsUrl(facebookApiUrl, digits, `/pages/${encodeURIComponent(pageId)}/forms`),
123183
+ config,
123184
+ buildCreateFormBody(cfg.form),
123185
+ opts.verbose
123186
+ );
123187
+ progress.formId = extractCreatedId(data);
123188
+ if (!progress.formId) throw new Error("\u5EFA\u8868\u5355\u6210\u529F\u4F46\u672A\u8FD4\u56DE id");
123189
+ }
123190
+ } catch (err) {
123191
+ progress.failedStep = "form";
123192
+ progress.error = formatMetaAdError(err);
123193
+ await emitProgress();
123194
+ console.error(
123195
+ `
123196
+ \u274C \u8868\u5355\u6B65\u9AA4\u5931\u8D25\uFF1A${progress.error}
123197
+ \u5DF2\u5EFA\u6210\uFF1A${summarizeProgress(progress)}
123198
+ `
123199
+ );
123200
+ process.exit(1);
123201
+ }
123202
+ try {
123203
+ const src = resolveCreateImageSource(cfg, opts.configFile);
123204
+ if (src.imageHash) {
123205
+ progress.imageHash = src.imageHash;
123206
+ } else {
123207
+ let bytes;
123208
+ if (src.imagePath) bytes = readImageBytesBase64(src.imagePath);
123209
+ const data = await postJson(
123210
+ adsUrl(facebookApiUrl, digits, "/images"),
123211
+ config,
123212
+ buildUploadImageBody({ name: src.name, imageUrl: src.imageUrl, bytesBase64: bytes }),
123213
+ opts.verbose
123214
+ );
123215
+ progress.imageHash = extractImageHash(data);
123216
+ if (!progress.imageHash) throw new Error("\u4F20\u56FE\u6210\u529F\u4F46\u672A\u8FD4\u56DE hash");
123217
+ }
123218
+ } catch (err) {
123219
+ progress.failedStep = "image";
123220
+ progress.error = formatMetaAdError(err);
123221
+ await emitProgress();
123222
+ console.error(`
123223
+ \u274C \u4F20\u56FE\u5931\u8D25\uFF1A${progress.error}
123224
+ \u5DF2\u5EFA\u6210\uFF1A${summarizeProgress(progress)}
123225
+ `);
123226
+ process.exit(1);
123227
+ }
123228
+ try {
123229
+ const data = await postJson(
123230
+ adsUrl(facebookApiUrl, digits, "/campaigns"),
123231
+ config,
123232
+ buildCreateCampaignBody({
123233
+ campaign: { ...cfg.campaign, status: omitActive(cfg.campaign.status) },
123234
+ dailyBudgetMinor: cfg.campaign.dailyBudget != null ? toMinorUnitString(cfg.campaign.dailyBudget, currency) : void 0,
123235
+ lifetimeBudgetMinor: cfg.campaign.lifetimeBudget != null ? toMinorUnitString(cfg.campaign.lifetimeBudget, currency) : void 0
123236
+ }),
123237
+ opts.verbose
123238
+ );
123239
+ progress.campaignId = extractCreatedId(data);
123240
+ if (!progress.campaignId) throw new Error("\u5EFA\u7CFB\u5217\u6210\u529F\u4F46\u672A\u8FD4\u56DE id");
123241
+ } catch (err) {
123242
+ progress.failedStep = "campaign";
123243
+ progress.error = formatMetaAdError(err);
123244
+ await emitProgress();
123245
+ console.error(
123246
+ `
123247
+ \u274C \u5EFA\u7CFB\u5217\u5931\u8D25\uFF1A${progress.error}
123248
+ \u5DF2\u5EFA\u6210\uFF1A${summarizeProgress(progress)}
123249
+ `
123250
+ );
123251
+ process.exit(1);
123252
+ }
123253
+ try {
123254
+ const adsetUrl = adsUrl(facebookApiUrl, digits, "/adsets");
123255
+ const baseAdset = {
123256
+ ...cfg.adset,
123257
+ optimizationGoal: cfg.adset.optimizationGoal?.toUpperCase(),
123258
+ status: omitActive(cfg.adset.status)
123259
+ };
123260
+ const money3 = {
123261
+ dailyBudgetMinor: cfg.adset.dailyBudget != null ? toMinorUnitString(cfg.adset.dailyBudget, currency) : void 0,
123262
+ lifetimeBudgetMinor: cfg.adset.lifetimeBudget != null ? toMinorUnitString(cfg.adset.lifetimeBudget, currency) : void 0
123263
+ };
123264
+ let data;
123265
+ try {
123266
+ data = await postJson(
123267
+ adsetUrl,
123268
+ config,
123269
+ buildCreateAdSetBody({
123270
+ adset: baseAdset,
123271
+ campaignId: progress.campaignId,
123272
+ pageId,
123273
+ ...money3,
123274
+ bidAmountMinor: cfg.adset.bidAmount != null ? toMinorUnitString(cfg.adset.bidAmount, currency) : void 0
123275
+ }),
123276
+ opts.verbose
123277
+ );
123278
+ } catch (firstErr) {
123279
+ if (!isBidAmountRequired(firstErr) || cfg.adset.bidAmount != null) throw firstErr;
123280
+ const retryMajor = cfg.campaign.dailyBudget ?? cfg.campaign.lifetimeBudget ?? cfg.adset.dailyBudget ?? cfg.adset.lifetimeBudget ?? 1;
123281
+ if (!opts.jsonOut) {
123282
+ console.warn(
123283
+ `
123284
+ \u26A0\uFE0F \u8BE5\u6237\u8981\u6C42 bid cap\uFF0C\u6309\u65E5/\u603B\u9884\u7B97 ${retryMajor} ${currency} \u5E26 LOWEST_COST_WITH_BID_CAP \u91CD\u8BD5\u5EFA\u7EC4
123285
+ `
123286
+ );
123287
+ }
123288
+ data = await postJson(
123289
+ adsetUrl,
123290
+ config,
123291
+ buildCreateAdSetBody({
123292
+ adset: { ...baseAdset, bidStrategy: "LOWEST_COST_WITH_BID_CAP" },
123293
+ campaignId: progress.campaignId,
123294
+ pageId,
123295
+ ...money3,
123296
+ bidAmountMinor: toMinorUnitString(retryMajor, currency)
123297
+ }),
123298
+ opts.verbose
123299
+ );
123300
+ }
123301
+ progress.adSetId = extractCreatedId(data);
123302
+ if (!progress.adSetId) throw new Error("\u5EFA\u7EC4\u6210\u529F\u4F46\u672A\u8FD4\u56DE id");
123303
+ } catch (err) {
123304
+ progress.failedStep = "adset";
123305
+ progress.error = formatMetaAdError(err);
123306
+ await emitProgress();
123307
+ console.error(
123308
+ `
123309
+ \u274C \u5EFA\u5E7F\u544A\u7EC4\u5931\u8D25\uFF1A${progress.error}
123310
+ \u5DF2\u5EFA\u6210\uFF1A${summarizeProgress(progress)}
123311
+ `
123312
+ );
123313
+ process.exit(1);
123314
+ }
123315
+ try {
123316
+ const data = await postJson(
123317
+ adsUrl(facebookApiUrl, digits, "/adcreatives"),
123318
+ config,
123319
+ buildCreateCreativeBody({
123320
+ creative: cfg.creative,
123321
+ pageId,
123322
+ formId: progress.formId,
123323
+ imageHash: progress.imageHash
123324
+ }),
123325
+ opts.verbose
123326
+ );
123327
+ progress.creativeId = extractCreatedId(data);
123328
+ if (!progress.creativeId) throw new Error("\u5EFA\u521B\u610F\u6210\u529F\u4F46\u672A\u8FD4\u56DE id");
123329
+ } catch (err) {
123330
+ progress.failedStep = "creative";
123331
+ progress.error = formatMetaAdError(err);
123332
+ await emitProgress();
123333
+ console.error(
123334
+ `
123335
+ \u274C \u5EFA\u521B\u610F\u5931\u8D25\uFF1A${progress.error}
123336
+ \u5DF2\u5EFA\u6210\uFF1A${summarizeProgress(progress)}
123337
+ `
123338
+ );
123339
+ process.exit(1);
123340
+ }
123341
+ try {
123342
+ const data = await postJson(
123343
+ adsUrl(facebookApiUrl, digits, "/ads"),
123344
+ config,
123345
+ buildCreateAdBody({
123346
+ name: cfg.ad.name.trim(),
123347
+ adsetId: progress.adSetId,
123348
+ creativeId: progress.creativeId,
123349
+ status: omitActive(cfg.ad.status)
123350
+ }),
123351
+ opts.verbose
123352
+ );
123353
+ progress.adId = extractCreatedId(data);
123354
+ if (!progress.adId) throw new Error("\u5EFA\u5E7F\u544A\u6210\u529F\u4F46\u672A\u8FD4\u56DE id");
123355
+ } catch (err) {
123356
+ progress.failedStep = "ad";
123357
+ progress.error = formatMetaAdError(err);
123358
+ await emitProgress();
123359
+ console.error(
123360
+ `
123361
+ \u274C \u5EFA\u5E7F\u544A\u5931\u8D25\uFF1A${progress.error}
123362
+ \u5DF2\u5EFA\u6210\uFF1A${summarizeProgress(progress)}
123363
+ `
123364
+ );
123365
+ process.exit(1);
123366
+ }
123367
+ progress.ok = true;
123368
+ if (await emitProgress()) return;
123369
+ console.log("\n\u2705 Meta Instant Form \u7EBF\u7D22\u5E7F\u544A\u5DF2\u521B\u5EFA\uFF08\u9ED8\u8BA4 PAUSED\uFF09\n");
123370
+ console.log(` formId ${progress.formId}`);
123371
+ console.log(` imageHash ${progress.imageHash}`);
123372
+ console.log(` campaignId ${progress.campaignId}`);
123373
+ console.log(` adSetId ${progress.adSetId}`);
123374
+ console.log(` creativeId ${progress.creativeId}`);
123375
+ console.log(` adId ${progress.adId}`);
123376
+ console.log(
123377
+ "\n \u6295\u653E\uFF1A\u4E09\u4E2A\u5BF9\u8C61\u5206\u522B meta-ad campaign-status / adset-status / ad-status --status ACTIVE\n"
123378
+ );
123379
+ }
123380
+ function omitActive(status) {
123381
+ if (!status) return void 0;
123382
+ const t = status.trim().toUpperCase();
123383
+ if (t === "ACTIVE") return void 0;
123384
+ return t === "PAUSED" ? "PAUSED" : status;
123385
+ }
123386
+ function summarizeProgress(p) {
123387
+ const parts = [
123388
+ p.formId && `formId=${p.formId}`,
123389
+ p.imageHash && `imageHash=${p.imageHash}`,
123390
+ p.campaignId && `campaignId=${p.campaignId}`,
123391
+ p.adSetId && `adSetId=${p.adSetId}`,
123392
+ p.creativeId && `creativeId=${p.creativeId}`,
123393
+ p.adId && `adId=${p.adId}`
123394
+ ].filter(Boolean);
123395
+ return parts.length > 0 ? parts.join(" ") : "\u65E0";
123396
+ }
123397
+
123398
+ // src/commands/meta-ad/pages.ts
123399
+ init_cli_table();
123400
+ async function runMetaAdPages(opts) {
123401
+ const { digits } = resolveMetaAccountId(opts.account);
123402
+ const { config, facebookApiUrl } = metaAdContext(opts.token);
123403
+ const raw = await metaAdGet(
123404
+ adsUrl(facebookApiUrl, digits, "/pages"),
123405
+ config,
123406
+ opts.verbose,
123407
+ "\u5217\u4E3B\u9875\u5931\u8D25"
123408
+ );
123409
+ const items2 = Array.isArray(raw) ? raw : [];
123410
+ const payload = wrapListJson({
123411
+ page: 1,
123412
+ pageSize: items2.length || 25,
123413
+ total: items2.length,
123414
+ items: items2
123415
+ });
123416
+ if (await emitMetaJson(opts, "meta-ad-pages", "meta-ad pages", payload, digits)) return;
123417
+ console.log(`
123418
+ \u53EF\u6295\u653E\u4E3B\u9875\uFF08\u8D26\u6237 ${digits}\uFF0C${items2.length} \u4E2A\uFF09
123419
+ `);
123420
+ if (items2.length === 0) {
123421
+ console.log(
123422
+ " \u7A7A\u5217\u8868\u3002\u5F53\u524D FacebookAds Token \u5217\u4E0D\u5230\u4E3B\u9875\uFF0C\u5148\u52A0\u4E3B\u9875\u5E7F\u544A\u6295\u653E/\u7BA1\u7406\u5458\u6743\u9650\u518D\u91CD\u65B0\u6388\u6743\u3002\n"
123423
+ );
123424
+ return;
123425
+ }
123426
+ printCliTable(
123427
+ items2.map((p) => {
123428
+ const rec = p;
123429
+ return {
123430
+ id: String(rec.id ?? ""),
123431
+ name: String(rec.name ?? "\u2014"),
123432
+ category: String(rec.category ?? "\u2014"),
123433
+ tasks: Array.isArray(rec.tasks) ? rec.tasks.join(",") : "\u2014"
123434
+ };
123435
+ }),
123436
+ [
123437
+ { key: "id", header: "pageId" },
123438
+ { key: "name", header: "\u540D\u79F0" },
123439
+ { key: "category", header: "\u7C7B\u76EE" },
123440
+ { key: "tasks", header: "\u4EFB\u52A1" }
123441
+ ]
123442
+ );
123443
+ console.log();
123444
+ }
123445
+
123446
+ // src/commands/meta-ad/forms.ts
123447
+ init_cli_table();
123448
+ async function runMetaAdForms(opts) {
123449
+ const { digits } = resolveMetaAccountId(opts.account);
123450
+ const { config, facebookApiUrl } = metaAdContext(opts.token);
123451
+ const params = new URLSearchParams();
123452
+ if (opts.limit) params.set("limit", opts.limit);
123453
+ if (opts.after) params.set("after", opts.after);
123454
+ if (opts.before) params.set("before", opts.before);
123455
+ const qs = params.toString();
123456
+ const path47 = `/pages/${encodeURIComponent(opts.pageId)}/forms${qs ? `?${qs}` : ""}`;
123457
+ const raw = await metaAdGet(
123458
+ adsUrl(facebookApiUrl, digits, path47),
123459
+ config,
123460
+ opts.verbose,
123461
+ "\u5217\u8868\u5355\u5931\u8D25"
123462
+ );
123463
+ if (await emitMetaJson(opts, "meta-ad-forms", "meta-ad forms", raw, digits)) return;
123464
+ const data = Array.isArray(raw.data) ? raw.data : [];
123465
+ console.log(`
123466
+ Instant Form\uFF08page ${opts.pageId}\uFF0C\u672C\u9875 ${data.length} \u6761\uFF09
123467
+ `);
123468
+ if (data.length === 0) {
123469
+ console.log(" \u6682\u65E0\u8868\u5355\u3002\u53EF\u7528 meta-ad form-create \u65B0\u5EFA\u3002\n");
123470
+ return;
123471
+ }
123472
+ printCliTable(
123473
+ data.map((row) => {
123474
+ const rec = row;
123475
+ return {
123476
+ id: String(rec.id ?? ""),
123477
+ name: String(rec.name ?? "\u2014"),
123478
+ status: String(rec.status ?? "\u2014"),
123479
+ locale: String(rec.locale ?? "\u2014")
123480
+ };
123481
+ }),
123482
+ [
123483
+ { key: "id", header: "formId" },
123484
+ { key: "name", header: "\u540D\u79F0" },
123485
+ { key: "status", header: "\u72B6\u6001" },
123486
+ { key: "locale", header: "\u8BED\u8A00" }
123487
+ ]
123488
+ );
123489
+ console.log();
123490
+ }
123491
+ function assertFormCreateable(form) {
123492
+ const fake = {
123493
+ account: "1",
123494
+ pageId: "1",
123495
+ budgetMode: "CBO",
123496
+ form,
123497
+ campaign: { name: "x", dailyBudget: 1 },
123498
+ adset: { name: "x", countries: ["US"] },
123499
+ creative: {
123500
+ name: "x",
123501
+ message: "x",
123502
+ link: "https://example.com",
123503
+ imageHash: "x"
123504
+ },
123505
+ ad: { name: "x" }
123506
+ };
123507
+ const { errors } = runMetaLeadValidation(fake);
123508
+ const formErrors = errors.filter((e) => e.startsWith("form."));
123509
+ if (form.reuseId?.trim()) {
123510
+ console.error("\n\u274C form-create \u662F\u65B0\u5EFA\u8868\u5355\uFF0C\u4E0D\u8981\u4F20 reuseId\n");
123511
+ process.exit(1);
123512
+ }
123513
+ if (!form.name?.trim()) {
123514
+ console.error("\n\u274C form.name \u5FC5\u586B\n");
123515
+ process.exit(1);
123516
+ }
123517
+ if (!form.privacyPolicyUrl?.trim() || !isHttpUrl(form.privacyPolicyUrl)) {
123518
+ console.error("\n\u274C form.privacyPolicyUrl \u987B\u4E3A http/https \u7EDD\u5BF9\u5730\u5740\n");
123519
+ process.exit(1);
123520
+ }
123521
+ if (formErrors.length > 0) {
123522
+ console.error("\n\u274C \u8868\u5355\u914D\u7F6E\u65E0\u6548\uFF1A");
123523
+ for (const e of formErrors) console.error(` \u2022 ${e}`);
123524
+ console.error();
123525
+ process.exit(1);
123526
+ }
123527
+ }
123528
+ async function runMetaAdFormCreate(opts) {
123529
+ const { digits } = resolveMetaAccountId(opts.account);
123530
+ const form = loadMetaLeadFormConfig(opts.configFile);
123531
+ assertFormCreateable(form);
123532
+ const { config, facebookApiUrl } = metaAdContext(opts.token);
123533
+ const body = buildCreateFormBody(form);
123534
+ const data = await metaAdPost(
123535
+ adsUrl(facebookApiUrl, digits, `/pages/${encodeURIComponent(opts.pageId)}/forms`),
123536
+ config,
123537
+ body,
123538
+ opts.verbose,
123539
+ "\u65B0\u5EFA\u8868\u5355\u5931\u8D25"
123540
+ );
123541
+ const id = extractCreatedId(data);
123542
+ const payload = { ...data, id };
123543
+ if (await emitMetaJson(opts, "meta-ad-form-create", "meta-ad form-create", payload, digits)) {
123544
+ return;
123545
+ }
123546
+ console.log(`
123547
+ \u2705 Instant Form \u5DF2\u521B\u5EFA\uFF1A${id ?? JSON.stringify(data)}
123548
+ `);
123549
+ }
123550
+
123551
+ // src/commands/meta-ad/campaign.ts
123552
+ init_cli_table();
123553
+ async function runMetaAdCampaignGet(opts) {
123554
+ const { digits } = resolveMetaAccountId(opts.account);
123555
+ const { config, facebookApiUrl } = metaAdContext(opts.token);
123556
+ const [raw, currency] = await Promise.all([
123557
+ metaAdGet(
123558
+ adsUrl(facebookApiUrl, digits, `/campaigns/${encodeURIComponent(opts.id)}`),
123559
+ config,
123560
+ opts.verbose,
123561
+ "\u8BFB\u53D6\u7CFB\u5217\u5931\u8D25"
123562
+ ),
123563
+ fetchAccountCurrency(facebookApiUrl, digits, config, opts.verbose)
123564
+ ]);
123565
+ const payload = withMoneyDisplay(raw, currency);
123566
+ if (await emitMetaJson(opts, "meta-ad-campaign", "meta-ad campaign", payload, digits)) return;
123567
+ console.log(`
123568
+ \u7CFB\u5217 ${opts.id}\uFF08${currency}\uFF09
123569
+ `);
123570
+ printCliTable(
123571
+ [
123572
+ {
123573
+ id: String(payload.id ?? opts.id),
123574
+ name: String(payload.name ?? "\u2014"),
123575
+ status: String(payload.status ?? "\u2014"),
123576
+ objective: String(payload.objective ?? "\u2014"),
123577
+ budget: formatYuanDisplay(payload.daily_budgetDisplay)
123578
+ }
123579
+ ],
123580
+ [
123581
+ { key: "id", header: "ID" },
123582
+ { key: "name", header: "\u540D\u79F0" },
123583
+ { key: "status", header: "\u72B6\u6001" },
123584
+ { key: "objective", header: "\u76EE\u6807" },
123585
+ { key: "budget", header: "\u65E5\u9884\u7B97(\u5143)" }
123586
+ ]
123587
+ );
123588
+ console.log();
123589
+ }
123590
+ async function runMetaAdCampaignCreate(opts) {
123591
+ const { digits } = resolveMetaAccountId(opts.account);
123592
+ if (!opts.name?.trim()) {
123593
+ console.error("\n\u274C --name \u5FC5\u586B\n");
123594
+ process.exit(1);
123595
+ }
123596
+ const daily = parseOptionalNumber(opts.dailyBudget);
123597
+ const lifetime = parseOptionalNumber(opts.lifetimeBudget);
123598
+ if (daily != null && lifetime != null) {
123599
+ console.error("\n\u274C --daily-budget \u4E0E --lifetime-budget \u4E0D\u80FD\u540C\u65F6\u4F20\n");
123600
+ process.exit(1);
123601
+ }
123602
+ const { config, facebookApiUrl } = metaAdContext(opts.token);
123603
+ const currency = await fetchAccountCurrency(facebookApiUrl, digits, config, opts.verbose);
123604
+ const campaign = {
123605
+ name: opts.name.trim(),
123606
+ dailyBudget: daily,
123607
+ lifetimeBudget: lifetime,
123608
+ specialAdCategories: parseCommaList(opts.specialAdCategories),
123609
+ status: opts.status
123610
+ };
123611
+ let body;
123612
+ try {
123613
+ body = buildCreateCampaignBody({
123614
+ campaign,
123615
+ dailyBudgetMinor: daily != null ? toMinorUnitString(daily, currency) : void 0,
123616
+ lifetimeBudgetMinor: lifetime != null ? toMinorUnitString(lifetime, currency) : void 0
123617
+ });
123618
+ } catch (err) {
123619
+ console.error(`
123620
+ \u274C ${err instanceof Error ? err.message : String(err)}
123621
+ `);
123622
+ process.exit(1);
123623
+ }
123624
+ const data = await metaAdPost(
123625
+ adsUrl(facebookApiUrl, digits, "/campaigns"),
123626
+ config,
123627
+ body,
123628
+ opts.verbose,
123629
+ "\u5EFA\u7CFB\u5217\u5931\u8D25"
123630
+ );
123631
+ const id = extractCreatedId(data);
123632
+ const payload = { ...data, id, currency };
123633
+ if (await emitMetaJson(opts, "meta-ad-campaign-create", "meta-ad campaign-create", payload, digits)) {
123634
+ return;
123635
+ }
123636
+ console.log(
123637
+ `
123638
+ \u2705 \u7CFB\u5217\u5DF2\u521B\u5EFA\uFF1A${id ?? JSON.stringify(data)}\uFF08\u9ED8\u8BA4 PAUSED\uFF0Cobjective=OUTCOME_LEADS\uFF09
123639
+ `
123640
+ );
123641
+ }
123642
+ async function runMetaAdCampaignEdit(opts) {
123643
+ const { digits } = resolveMetaAccountId(opts.account);
123644
+ const daily = parseOptionalNumber(opts.dailyBudget);
123645
+ const lifetime = parseOptionalNumber(opts.lifetimeBudget);
123646
+ if (daily != null && lifetime != null) {
123647
+ console.error("\n\u274C --daily-budget \u4E0E --lifetime-budget \u4E0D\u80FD\u540C\u65F6\u4F20\n");
123648
+ process.exit(1);
123649
+ }
123650
+ const { config, facebookApiUrl } = metaAdContext(opts.token);
123651
+ const currency = await fetchAccountCurrency(facebookApiUrl, digits, config, opts.verbose);
123652
+ let body;
123653
+ try {
123654
+ body = buildUpdateCampaignBody({
123655
+ name: opts.name?.trim() || void 0,
123656
+ dailyBudgetMinor: daily != null ? toMinorUnitString(daily, currency) : void 0,
123657
+ lifetimeBudgetMinor: lifetime != null ? toMinorUnitString(lifetime, currency) : void 0,
123658
+ bidStrategy: opts.bidStrategy,
123659
+ spendCapMinor: opts.spendCap != null ? toMinorUnitString(Number(opts.spendCap), currency) : void 0,
123660
+ startTime: opts.startTime,
123661
+ stopTime: opts.stopTime,
123662
+ specialAdCategories: opts.specialAdCategories != null ? parseCommaList(opts.specialAdCategories) : void 0,
123663
+ status: opts.status
123664
+ });
123665
+ } catch (err) {
123666
+ console.error(`
123667
+ \u274C ${err instanceof Error ? err.message : String(err)}
123668
+ `);
123669
+ process.exit(1);
123670
+ }
123671
+ if (Object.keys(body).length === 0) {
123672
+ console.error("\n\u274C \u81F3\u5C11\u4F20\u4E00\u4E2A\u53EF\u6539\u5B57\u6BB5\n");
123673
+ process.exit(1);
123674
+ }
123675
+ const data = await metaAdPost(
123676
+ adsUrl(facebookApiUrl, digits, `/campaigns/${encodeURIComponent(opts.id)}`),
123677
+ config,
123678
+ body,
123679
+ opts.verbose,
123680
+ "\u6539\u7CFB\u5217\u5931\u8D25"
123681
+ );
123682
+ const payload = { id: extractCreatedId(data) ?? opts.id, ...data };
123683
+ if (await emitMetaJson(opts, "meta-ad-campaign-edit", "meta-ad campaign-edit", payload, digits)) {
123684
+ return;
123685
+ }
123686
+ console.log(`
123687
+ \u2705 \u7CFB\u5217 ${opts.id} \u5DF2\u66F4\u65B0
123688
+ `);
123689
+ }
123690
+ async function runMetaAdCampaignStatus(opts) {
123691
+ const { digits } = resolveMetaAccountId(opts.account);
123692
+ if (!opts.status?.trim()) {
123693
+ console.error("\n\u274C --status \u5FC5\u586B\uFF08ACTIVE / PAUSED / DELETED / ARCHIVED\uFF09\n");
123694
+ process.exit(1);
123695
+ }
123696
+ const { config, facebookApiUrl } = metaAdContext(opts.token);
123697
+ const data = await metaAdPost(
123698
+ adsUrl(facebookApiUrl, digits, `/campaigns/${encodeURIComponent(opts.id)}/status`),
123699
+ config,
123700
+ buildStatusBody(opts.status),
123701
+ opts.verbose,
123702
+ "\u6539\u7CFB\u5217\u72B6\u6001\u5931\u8D25"
123703
+ );
123704
+ const payload = { id: extractCreatedId(data) ?? opts.id, status: opts.status.trim(), ...data };
123705
+ if (await emitMetaJson(opts, "meta-ad-campaign-status", "meta-ad campaign-status", payload, digits)) {
123706
+ return;
123707
+ }
123708
+ console.log(`
123709
+ \u2705 \u7CFB\u5217 ${opts.id} \u72B6\u6001\u5DF2\u8BBE\u4E3A ${opts.status.trim()}
123710
+ `);
123711
+ }
123712
+
123713
+ // src/commands/meta-ad/adset.ts
123714
+ init_cli_table();
123715
+ function parseGenders(raw) {
123716
+ if (!raw?.trim()) return void 0;
123717
+ const nums = raw.split(",").map((s) => Number(s.trim()));
123718
+ if (nums.some((n) => n !== 1 && n !== 2)) {
123719
+ throw new Error("--genders \u53EA\u5141\u8BB8 1\uFF08\u7537\uFF09/ 2\uFF08\u5973\uFF09\uFF0C\u9017\u53F7\u5206\u9694");
123720
+ }
123721
+ return nums;
123722
+ }
123723
+ function assertCountries(raw) {
123724
+ const countries = parseCommaList(raw);
123725
+ if (countries.length === 0) {
123726
+ throw new Error("--countries \u81F3\u5C11\u4E00\u4E2A ISO \u4E24\u4F4D\u56FD\u5BB6\uFF08\u5982 US,CN\uFF09");
123727
+ }
123728
+ for (const c of countries) {
123729
+ if (!isIsoCountry(c)) throw new Error(`\u975E\u6CD5\u56FD\u5BB6\u7801\uFF1A${c}`);
123730
+ }
123731
+ return countries;
123732
+ }
123733
+ async function runMetaAdAdsetGet(opts) {
123734
+ const { digits } = resolveMetaAccountId(opts.account);
123735
+ const { config, facebookApiUrl } = metaAdContext(opts.token);
123736
+ const [raw, currency] = await Promise.all([
123737
+ metaAdGet(
123738
+ adsUrl(facebookApiUrl, digits, `/adsets/${encodeURIComponent(opts.id)}`),
123739
+ config,
123740
+ opts.verbose,
123741
+ "\u8BFB\u53D6\u5E7F\u544A\u7EC4\u5931\u8D25"
123742
+ ),
123743
+ fetchAccountCurrency(facebookApiUrl, digits, config, opts.verbose)
123744
+ ]);
123745
+ const payload = withMoneyDisplay(raw, currency);
123746
+ if (await emitMetaJson(opts, "meta-ad-adset", "meta-ad adset", payload, digits)) return;
123747
+ console.log(`
123748
+ \u5E7F\u544A\u7EC4 ${opts.id}\uFF08${currency}\uFF09
123749
+ `);
123750
+ printCliTable(
123751
+ [
123752
+ {
123753
+ id: String(payload.id ?? opts.id),
123754
+ name: String(payload.name ?? "\u2014"),
123755
+ status: String(payload.status ?? "\u2014"),
123756
+ goal: String(payload.optimization_goal ?? "\u2014"),
123757
+ budget: formatYuanDisplay(payload.daily_budgetDisplay)
123758
+ }
123759
+ ],
123760
+ [
123761
+ { key: "id", header: "ID" },
123762
+ { key: "name", header: "\u540D\u79F0" },
123763
+ { key: "status", header: "\u72B6\u6001" },
123764
+ { key: "goal", header: "\u4F18\u5316\u76EE\u6807" },
123765
+ { key: "budget", header: "\u65E5\u9884\u7B97(\u5143)" }
123766
+ ]
123767
+ );
123768
+ console.log();
123769
+ }
123770
+ async function runMetaAdAdsetCreate(opts) {
123771
+ const { digits } = resolveMetaAccountId(opts.account);
123772
+ if (!opts.name?.trim() || !opts.campaignId?.trim() || !opts.pageId?.trim()) {
123773
+ console.error("\n\u274C --name / --campaign-id / --page-id \u5FC5\u586B\n");
123774
+ process.exit(1);
123775
+ }
123776
+ let countries;
123777
+ let genders;
123778
+ try {
123779
+ countries = assertCountries(opts.countries);
123780
+ genders = parseGenders(opts.genders);
123781
+ } catch (err) {
123782
+ console.error(`
123783
+ \u274C ${err instanceof Error ? err.message : String(err)}
123784
+ `);
123785
+ process.exit(1);
123786
+ }
123787
+ const daily = parseOptionalNumber(opts.dailyBudget);
123788
+ const lifetime = parseOptionalNumber(opts.lifetimeBudget);
123789
+ if (daily != null && lifetime != null) {
123790
+ console.error("\n\u274C --daily-budget \u4E0E --lifetime-budget \u4E0D\u80FD\u540C\u65F6\u4F20\n");
123791
+ process.exit(1);
123792
+ }
123793
+ if (opts.optimizationGoal) {
123794
+ const g = opts.optimizationGoal.trim().toUpperCase();
123795
+ if (!META_OPTIMIZATION_GOALS.includes(g)) {
123796
+ console.error("\n\u274C --optimization-goal \u53EA\u5141\u8BB8 LEAD_GENERATION / QUALITY_LEAD\n");
123797
+ process.exit(1);
123798
+ }
123799
+ }
123800
+ const { config, facebookApiUrl } = metaAdContext(opts.token);
123801
+ const currency = await fetchAccountCurrency(facebookApiUrl, digits, config, opts.verbose);
123802
+ const adset = {
123803
+ name: opts.name.trim(),
123804
+ countries,
123805
+ ageMin: parseOptionalNumber(opts.ageMin),
123806
+ ageMax: parseOptionalNumber(opts.ageMax),
123807
+ genders,
123808
+ optimizationGoal: opts.optimizationGoal?.trim().toUpperCase(),
123809
+ bidStrategy: opts.bidStrategy,
123810
+ bidAmount: parseOptionalNumber(opts.bidAmount),
123811
+ startTime: opts.startTime,
123812
+ endTime: opts.endTime,
123813
+ status: opts.status
123814
+ };
123815
+ let body;
123816
+ try {
123817
+ body = buildCreateAdSetBody({
123818
+ adset,
123819
+ campaignId: opts.campaignId.trim(),
123820
+ pageId: opts.pageId.trim(),
123821
+ dailyBudgetMinor: daily != null ? toMinorUnitString(daily, currency) : void 0,
123822
+ lifetimeBudgetMinor: lifetime != null ? toMinorUnitString(lifetime, currency) : void 0,
123823
+ bidAmountMinor: adset.bidAmount != null ? toMinorUnitString(adset.bidAmount, currency) : void 0
123824
+ });
123825
+ } catch (err) {
123826
+ console.error(`
123827
+ \u274C ${err instanceof Error ? err.message : String(err)}
123828
+ `);
123829
+ process.exit(1);
123830
+ }
123831
+ const data = await metaAdPost(
123832
+ adsUrl(facebookApiUrl, digits, "/adsets"),
123833
+ config,
123834
+ body,
123835
+ opts.verbose,
123836
+ "\u5EFA\u5E7F\u544A\u7EC4\u5931\u8D25"
123837
+ );
123838
+ const id = extractCreatedId(data);
123839
+ const payload = { ...data, id, currency };
123840
+ if (await emitMetaJson(opts, "meta-ad-adset-create", "meta-ad adset-create", payload, digits)) {
123841
+ return;
123842
+ }
123843
+ console.log(`
123844
+ \u2705 \u5E7F\u544A\u7EC4\u5DF2\u521B\u5EFA\uFF1A${id ?? JSON.stringify(data)}
123845
+ `);
123846
+ }
123847
+ async function runMetaAdAdsetEdit(opts) {
123848
+ const { digits } = resolveMetaAccountId(opts.account);
123849
+ const daily = parseOptionalNumber(opts.dailyBudget);
123850
+ const lifetime = parseOptionalNumber(opts.lifetimeBudget);
123851
+ if (daily != null && lifetime != null) {
123852
+ console.error("\n\u274C --daily-budget \u4E0E --lifetime-budget \u4E0D\u80FD\u540C\u65F6\u4F20\n");
123853
+ process.exit(1);
123854
+ }
123855
+ let countries;
123856
+ let genders;
123857
+ try {
123858
+ if (opts.countries != null) countries = assertCountries(opts.countries);
123859
+ genders = parseGenders(opts.genders);
123860
+ } catch (err) {
123861
+ console.error(`
123862
+ \u274C ${err instanceof Error ? err.message : String(err)}
123863
+ `);
123864
+ process.exit(1);
123865
+ }
123866
+ if (opts.optimizationGoal) {
123867
+ const g = opts.optimizationGoal.trim().toUpperCase();
123868
+ if (!META_OPTIMIZATION_GOALS.includes(g)) {
123869
+ console.error("\n\u274C --optimization-goal \u53EA\u5141\u8BB8 LEAD_GENERATION / QUALITY_LEAD\n");
123870
+ process.exit(1);
123871
+ }
123872
+ }
123873
+ const { config, facebookApiUrl } = metaAdContext(opts.token);
123874
+ const currency = await fetchAccountCurrency(facebookApiUrl, digits, config, opts.verbose);
123875
+ const bidAmount = parseOptionalNumber(opts.bidAmount);
123876
+ let body;
123877
+ try {
123878
+ body = buildUpdateAdSetBody({
123879
+ name: opts.name?.trim() || void 0,
123880
+ dailyBudgetMinor: daily != null ? toMinorUnitString(daily, currency) : void 0,
123881
+ lifetimeBudgetMinor: lifetime != null ? toMinorUnitString(lifetime, currency) : void 0,
123882
+ countries,
123883
+ ageMin: parseOptionalNumber(opts.ageMin),
123884
+ ageMax: parseOptionalNumber(opts.ageMax),
123885
+ genders,
123886
+ optimizationGoal: opts.optimizationGoal?.trim().toUpperCase(),
123887
+ bidStrategy: opts.bidStrategy,
123888
+ bidAmountMinor: bidAmount != null ? toMinorUnitString(bidAmount, currency) : void 0,
123889
+ startTime: opts.startTime,
123890
+ endTime: opts.endTime,
123891
+ status: opts.status
123892
+ });
123893
+ } catch (err) {
123894
+ console.error(`
123895
+ \u274C ${err instanceof Error ? err.message : String(err)}
123896
+ `);
123897
+ process.exit(1);
123898
+ }
123899
+ if (Object.keys(body).length === 0) {
123900
+ console.error("\n\u274C \u81F3\u5C11\u4F20\u4E00\u4E2A\u53EF\u6539\u5B57\u6BB5\n");
123901
+ process.exit(1);
123902
+ }
123903
+ const data = await metaAdPost(
123904
+ adsUrl(facebookApiUrl, digits, `/adsets/${encodeURIComponent(opts.id)}`),
123905
+ config,
123906
+ body,
123907
+ opts.verbose,
123908
+ "\u6539\u5E7F\u544A\u7EC4\u5931\u8D25"
123909
+ );
123910
+ const payload = { id: extractCreatedId(data) ?? opts.id, ...data };
123911
+ if (await emitMetaJson(opts, "meta-ad-adset-edit", "meta-ad adset-edit", payload, digits)) return;
123912
+ console.log(`
123913
+ \u2705 \u5E7F\u544A\u7EC4 ${opts.id} \u5DF2\u66F4\u65B0
123914
+ `);
123915
+ }
123916
+ async function runMetaAdAdsetStatus(opts) {
123917
+ const { digits } = resolveMetaAccountId(opts.account);
123918
+ if (!opts.status?.trim()) {
123919
+ console.error("\n\u274C --status \u5FC5\u586B\uFF08ACTIVE / PAUSED / DELETED / ARCHIVED\uFF09\n");
123920
+ process.exit(1);
123921
+ }
123922
+ const { config, facebookApiUrl } = metaAdContext(opts.token);
123923
+ const data = await metaAdPost(
123924
+ adsUrl(facebookApiUrl, digits, `/adsets/${encodeURIComponent(opts.id)}/status`),
123925
+ config,
123926
+ buildStatusBody(opts.status),
123927
+ opts.verbose,
123928
+ "\u6539\u5E7F\u544A\u7EC4\u72B6\u6001\u5931\u8D25"
123929
+ );
123930
+ const payload = { id: extractCreatedId(data) ?? opts.id, status: opts.status.trim(), ...data };
123931
+ if (await emitMetaJson(opts, "meta-ad-adset-status", "meta-ad adset-status", payload, digits)) {
123932
+ return;
123933
+ }
123934
+ console.log(`
123935
+ \u2705 \u5E7F\u544A\u7EC4 ${opts.id} \u72B6\u6001\u5DF2\u8BBE\u4E3A ${opts.status.trim()}
123936
+ `);
123937
+ }
123938
+
123939
+ // src/commands/meta-ad/creative.ts
123940
+ async function runMetaAdCreativeCreate(opts) {
123941
+ const { digits } = resolveMetaAccountId(opts.account);
123942
+ if (!opts.pageId?.trim() || !opts.name?.trim() || !opts.message?.trim() || !opts.link?.trim() || !opts.imageHash?.trim() || !opts.formId?.trim()) {
123943
+ console.error("\n\u274C --page-id / --name / --message / --link / --image-hash / --form-id \u5FC5\u586B\n");
123944
+ process.exit(1);
123945
+ }
123946
+ if (!isHttpUrl(opts.link.trim())) {
123947
+ console.error("\n\u274C --link \u987B\u4E3A http/https \u7EDD\u5BF9\u5730\u5740\n");
123948
+ process.exit(1);
123949
+ }
123950
+ const { config, facebookApiUrl } = metaAdContext(opts.token);
123951
+ const body = buildCreateCreativeBody({
123952
+ creative: {
123953
+ name: opts.name.trim(),
123954
+ message: opts.message,
123955
+ link: opts.link.trim(),
123956
+ headline: opts.headline,
123957
+ ctaType: opts.ctaType
123958
+ },
123959
+ pageId: opts.pageId.trim(),
123960
+ formId: opts.formId.trim(),
123961
+ imageHash: opts.imageHash.trim()
123962
+ });
123963
+ const data = await metaAdPost(
123964
+ adsUrl(facebookApiUrl, digits, "/adcreatives"),
123965
+ config,
123966
+ body,
123967
+ opts.verbose,
123968
+ "\u5EFA\u521B\u610F\u5931\u8D25"
123969
+ );
123970
+ const id = extractCreatedId(data);
123971
+ const payload = { ...data, id };
123972
+ if (await emitMetaJson(opts, "meta-ad-creative-create", "meta-ad creative-create", payload, digits)) {
123973
+ return;
123974
+ }
123975
+ console.log(
123976
+ `
123977
+ \u2705 \u521B\u610F\u5DF2\u521B\u5EFA\uFF1A${id ?? JSON.stringify(data)}\uFF08\u65E0\u5C31\u5730\u6539\u53E3\uFF0C\u6362\u56FE/\u6587\u6848/\u8868\u5355\u987B\u65B0\u5EFA\uFF09
123978
+ `
123979
+ );
123980
+ }
123981
+
123982
+ // src/commands/meta-ad/ad.ts
123983
+ init_cli_table();
123984
+ async function runMetaAdGet(opts) {
123985
+ const { digits } = resolveMetaAccountId(opts.account);
123986
+ const { config, facebookApiUrl } = metaAdContext(opts.token);
123987
+ const raw = await metaAdGet(
123988
+ adsUrl(facebookApiUrl, digits, `/ads/${encodeURIComponent(opts.id)}`),
123989
+ config,
123990
+ opts.verbose,
123991
+ "\u8BFB\u53D6\u5E7F\u544A\u5931\u8D25"
123992
+ );
123993
+ if (await emitMetaJson(opts, "meta-ad-ad", "meta-ad ad", raw, digits)) return;
123994
+ const creative = raw.creative ?? {};
123995
+ console.log(`
123996
+ \u5E7F\u544A ${opts.id}
123997
+ `);
123998
+ printCliTable(
123999
+ [
124000
+ {
124001
+ id: String(raw.id ?? opts.id),
124002
+ name: String(raw.name ?? "\u2014"),
124003
+ status: String(raw.status ?? "\u2014"),
124004
+ adset: String(raw.adset_id ?? "\u2014"),
124005
+ creative: String(creative.id ?? "\u2014")
124006
+ }
124007
+ ],
124008
+ [
124009
+ { key: "id", header: "ID" },
124010
+ { key: "name", header: "\u540D\u79F0" },
124011
+ { key: "status", header: "\u72B6\u6001" },
124012
+ { key: "adset", header: "\u5E7F\u544A\u7EC4" },
124013
+ { key: "creative", header: "\u521B\u610F" }
124014
+ ]
124015
+ );
124016
+ console.log();
124017
+ }
124018
+ async function runMetaAdCreate2(opts) {
124019
+ const { digits } = resolveMetaAccountId(opts.account);
124020
+ if (!opts.name?.trim() || !opts.adsetId?.trim() || !opts.creativeId?.trim()) {
124021
+ console.error("\n\u274C --name / --adset-id / --creative-id \u5FC5\u586B\n");
124022
+ process.exit(1);
124023
+ }
124024
+ const { config, facebookApiUrl } = metaAdContext(opts.token);
124025
+ const body = buildCreateAdBody({
124026
+ name: opts.name.trim(),
124027
+ adsetId: opts.adsetId.trim(),
124028
+ creativeId: opts.creativeId.trim(),
124029
+ status: opts.status
124030
+ });
124031
+ const data = await metaAdPost(
124032
+ adsUrl(facebookApiUrl, digits, "/ads"),
124033
+ config,
124034
+ body,
124035
+ opts.verbose,
124036
+ "\u5EFA\u5E7F\u544A\u5931\u8D25"
124037
+ );
124038
+ const id = extractCreatedId(data);
124039
+ const payload = { ...data, id };
124040
+ if (await emitMetaJson(opts, "meta-ad-ad-create", "meta-ad ad-create", payload, digits)) return;
124041
+ console.log(`
124042
+ \u2705 \u5E7F\u544A\u5DF2\u521B\u5EFA\uFF1A${id ?? JSON.stringify(data)}
124043
+ `);
124044
+ }
124045
+ async function runMetaAdEdit(opts) {
124046
+ const { digits } = resolveMetaAccountId(opts.account);
124047
+ const body = buildUpdateAdBody({
124048
+ name: opts.name?.trim() || void 0,
124049
+ creativeId: opts.creativeId?.trim() || void 0,
124050
+ status: opts.status
124051
+ });
124052
+ if (Object.keys(body).length === 0) {
124053
+ console.error("\n\u274C \u81F3\u5C11\u4F20 --name / --creative-id / --status \u4E4B\u4E00\n");
124054
+ process.exit(1);
124055
+ }
124056
+ const { config, facebookApiUrl } = metaAdContext(opts.token);
124057
+ const data = await metaAdPost(
124058
+ adsUrl(facebookApiUrl, digits, `/ads/${encodeURIComponent(opts.id)}`),
124059
+ config,
124060
+ body,
124061
+ opts.verbose,
124062
+ "\u6539\u5E7F\u544A\u5931\u8D25"
124063
+ );
124064
+ const payload = { id: extractCreatedId(data) ?? opts.id, ...data };
124065
+ if (await emitMetaJson(opts, "meta-ad-ad-edit", "meta-ad ad-edit", payload, digits)) return;
124066
+ console.log(`
124067
+ \u2705 \u5E7F\u544A ${opts.id} \u5DF2\u66F4\u65B0
124068
+ `);
124069
+ }
124070
+ async function runMetaAdStatus(opts) {
124071
+ const { digits } = resolveMetaAccountId(opts.account);
124072
+ if (!opts.status?.trim()) {
124073
+ console.error("\n\u274C --status \u5FC5\u586B\uFF08ACTIVE / PAUSED / DELETED / ARCHIVED\uFF09\n");
124074
+ process.exit(1);
124075
+ }
124076
+ const { config, facebookApiUrl } = metaAdContext(opts.token);
124077
+ const data = await metaAdPost(
124078
+ adsUrl(facebookApiUrl, digits, `/ads/${encodeURIComponent(opts.id)}/status`),
124079
+ config,
124080
+ buildStatusBody(opts.status),
124081
+ opts.verbose,
124082
+ "\u6539\u5E7F\u544A\u72B6\u6001\u5931\u8D25"
124083
+ );
124084
+ const payload = { id: extractCreatedId(data) ?? opts.id, status: opts.status.trim(), ...data };
124085
+ if (await emitMetaJson(opts, "meta-ad-ad-status", "meta-ad ad-status", payload, digits)) return;
124086
+ console.log(`
124087
+ \u2705 \u5E7F\u544A ${opts.id} \u72B6\u6001\u5DF2\u8BBE\u4E3A ${opts.status.trim()}
124088
+ `);
124089
+ }
124090
+
124091
+ // src/commands/meta-ad/_register.ts
124092
+ var JSON_OUT_HELP = "\u843D\u76D8\uFF08\u76EE\u5F55\u6216 *.json \u6587\u4EF6\u8DEF\u5F84\uFF09\u5E76\u66F4\u65B0 cli-manifest[-<\u67E5\u8BE2id>].json\uFF1Bstdout \u4E00\u884C\u6458\u8981";
124093
+ function addCommon(cmd) {
124094
+ return cmd.option("-t, --token <token>", "Auth Token").option("--json-out <path>", JSON_OUT_HELP, void 0).option("--verbose", "\u8BE6\u7EC6\u9519\u8BEF\u4FE1\u606F", false);
124095
+ }
124096
+ function addAccount(cmd) {
124097
+ return cmd.requiredOption(
124098
+ "-a, --account <id>",
124099
+ "Meta \u5E7F\u544A\u8D26\u6237 mediaCustomerId\uFF08\u6570\u5B57\u6216 act_<\u6570\u5B57>\uFF09"
124100
+ );
124101
+ }
124102
+ function register22(program2) {
124103
+ const meta = program2.command("meta-ad").description(
124104
+ "Meta / Facebook Instant Form \u7EBF\u7D22\u5E7F\u544A\uFF08FacebookAPI\uFF0C\u5730\u5740\u4ECE TSO API \u63A8\u5BFC\uFF09\n \u4EC5 OUTCOME_LEADS\uFF1B\u4E3B\u8DEF\u5F84\uFF1Avalidate \u2192 create\uFF1B\u5B58\u91CF\u7528\u539F\u8BED\u7EED\u8DD1 / \u6539\u72B6\u6001"
124105
+ );
124106
+ addCommon(
124107
+ meta.command("validate").description("\u672C\u5730\u6821\u9A8C Lead Ad JSON\uFF08\u4E0D\u8C03\u7F51\u5173\uFF09").requiredOption(
124108
+ "--config-file <path>",
124109
+ "JSON \u914D\u7F6E\uFF08\u89C1 assets/meta-lead-create-template.json\uFF09"
124110
+ )
124111
+ ).action(async (opts) => {
124112
+ await runMetaAdValidate(opts);
124113
+ });
124114
+ addCommon(
124115
+ meta.command("create").description("\u6309 JSON \u7F16\u6392\uFF1A\u8868\u5355\u2192\u56FE\u2192\u7CFB\u5217\u2192\u7EC4\u2192\u521B\u610F\u2192\u5E7F\u544A\uFF08\u9ED8\u8BA4 PAUSED\uFF1B\u5931\u8D25\u4E0D\u56DE\u6EDA\uFF09").requiredOption("--config-file <path>", "JSON \u914D\u7F6E\uFF08account \u5199\u5728\u6587\u4EF6\u91CC\uFF09")
124116
+ ).action(
124117
+ async (opts) => {
124118
+ await runMetaAdCreate(opts);
124119
+ }
124120
+ );
124121
+ addCommon(addAccount(meta.command("account").description("\u8BFB\u5E7F\u544A\u8D26\u6237\uFF08\u4F59\u989D/\u82B1\u8D39/\u5E01\u79CD\uFF09"))).action(
124122
+ async (opts) => {
124123
+ await runMetaAdAccount(opts);
124124
+ }
124125
+ );
124126
+ addCommon(
124127
+ addAccount(meta.command("pages").description("\u5217 Ads Token \u4E0B\u53EF\u6295\u653E\u4E3B\u9875\uFF08\u4E0D\u542B Page Token\uFF09"))
124128
+ ).action(
124129
+ async (opts) => {
124130
+ await runMetaAdPages(opts);
124131
+ }
124132
+ );
124133
+ addCommon(
124134
+ addAccount(
124135
+ meta.command("forms").description("\u5217\u67D0\u4E3B\u9875 Instant Form").requiredOption("--page-id <id>", "Facebook \u4E3B\u9875 ID").option("--limit <n>", "\u5206\u9875\u5927\u5C0F").option("--after <cursor>", "\u5411\u540E\u7FFB\u9875").option("--before <cursor>", "\u5411\u524D\u7FFB\u9875")
124136
+ )
124137
+ ).action(
124138
+ async (opts) => {
124139
+ await runMetaAdForms(opts);
124140
+ }
124141
+ );
124142
+ addCommon(
124143
+ addAccount(
124144
+ meta.command("form-create").description("\u65B0\u5EFA Instant Form\uFF08\u5EFA\u6210\u540E\u51E0\u4E4E\u4E0D\u53EF\u6539\uFF09").requiredOption("--page-id <id>", "Facebook \u4E3B\u9875 ID").requiredOption("--config-file <path>", "\u8868\u5355 JSON\uFF0C\u6216\u542B form \u6BB5\u7684 create JSON")
124145
+ )
124146
+ ).action(
124147
+ async (opts) => {
124148
+ await runMetaAdFormCreate(opts);
124149
+ }
124150
+ );
124151
+ addCommon(
124152
+ addAccount(
124153
+ meta.command("image-upload").description("\u4E0A\u4F20\u5E7F\u544A\u56FE\uFF0C\u8FD4\u56DE hash\uFF08\u521B\u610F\u5FC5\u987B\u7528 hash\uFF09").option("--name <name>", "\u6587\u4EF6\u540D").option("--image-url <url>", "\u516C\u7F51\u56FE\u7247 URL").option("--image-path <path>", "\u672C\u5730\u56FE\u7247\u8DEF\u5F84")
124154
+ )
124155
+ ).action(
124156
+ async (opts) => {
124157
+ await runMetaAdImageUpload(opts);
124158
+ }
124159
+ );
124160
+ addCommon(
124161
+ addAccount(
124162
+ meta.command("campaign").description("\u6309 ID \u8BFB\u7CFB\u5217\uFF08\u65E0\u5217\u8868\u53E3\uFF09").requiredOption("--id <campaignId>", "\u7CFB\u5217 ID")
124163
+ )
124164
+ ).action(
124165
+ async (opts) => {
124166
+ await runMetaAdCampaignGet(opts);
124167
+ }
124168
+ );
124169
+ addCommon(
124170
+ addAccount(
124171
+ meta.command("campaign-create").description("\u5EFA\u7EBF\u7D22\u7CFB\u5217\uFF08objective \u56FA\u5B9A OUTCOME_LEADS\uFF1B\u9ED8\u8BA4 PAUSED\uFF09").requiredOption("--name <name>", "\u7CFB\u5217\u540D\u79F0").option("--daily-budget <amount>", "\u65E5\u9884\u7B97\uFF0C\u4E3B\u5E01\u79CD\u5143\uFF08CBO\uFF09", parseFloat).option("--lifetime-budget <amount>", "\u603B\u9884\u7B97\uFF0C\u4E3B\u5E01\u79CD\u5143", parseFloat).option("--special-ad-categories <list>", "\u7279\u6B8A\u7C7B\u76EE\uFF0C\u9017\u53F7\u5206\u9694").option("--status <status>", "ACTIVE | PAUSED")
124172
+ )
124173
+ ).action(
124174
+ async (opts) => {
124175
+ await runMetaAdCampaignCreate(opts);
124176
+ }
124177
+ );
124178
+ addCommon(
124179
+ addAccount(
124180
+ meta.command("campaign-edit").description("\u6539\u7CFB\u5217\uFF08\u53EA\u4F20\u8981\u6539\u7684\u5B57\u6BB5\uFF1B\u4E0D\u80FD\u5207 CBO/ABO\uFF09").requiredOption("--id <campaignId>", "\u7CFB\u5217 ID").option("--name <name>", "\u65B0\u540D\u79F0").option("--daily-budget <amount>", "\u65E5\u9884\u7B97\uFF0C\u4E3B\u5E01\u79CD\u5143", parseFloat).option("--lifetime-budget <amount>", "\u603B\u9884\u7B97\uFF0C\u4E3B\u5E01\u79CD\u5143", parseFloat).option("--bid-strategy <s>", "\u51FA\u4EF7\u7B56\u7565").option("--spend-cap <amount>", "\u82B1\u8D39\u4E0A\u9650\uFF0C\u4E3B\u5E01\u79CD\u5143", parseFloat).option("--start-time <iso>", "\u5F00\u59CB\u65F6\u95F4").option("--stop-time <iso>", "\u7ED3\u675F\u65F6\u95F4").option("--special-ad-categories <list>", "\u7279\u6B8A\u7C7B\u76EE\uFF0C\u9017\u53F7\u5206\u9694").option("--status <status>", "\u72B6\u6001")
124181
+ )
124182
+ ).action(
124183
+ async (opts) => {
124184
+ await runMetaAdCampaignEdit(opts);
124185
+ }
124186
+ );
124187
+ addCommon(
124188
+ addAccount(
124189
+ meta.command("campaign-status").description("\u6539\u7CFB\u5217\u72B6\u6001\uFF08ACTIVE / PAUSED / DELETED / ARCHIVED\uFF09").requiredOption("--id <campaignId>", "\u7CFB\u5217 ID").requiredOption("--status <status>", "\u76EE\u6807\u72B6\u6001")
124190
+ )
124191
+ ).action(
124192
+ async (opts) => {
124193
+ await runMetaAdCampaignStatus(opts);
124194
+ }
124195
+ );
124196
+ addCommon(
124197
+ addAccount(
124198
+ meta.command("adset").description("\u6309 ID \u8BFB\u5E7F\u544A\u7EC4").requiredOption("--id <adSetId>", "\u5E7F\u544A\u7EC4 ID")
124199
+ )
124200
+ ).action(
124201
+ async (opts) => {
124202
+ await runMetaAdAdsetGet(opts);
124203
+ }
124204
+ );
124205
+ addCommon(
124206
+ addAccount(
124207
+ meta.command("adset-create").description("\u5EFA\u7EBF\u7D22\u5E7F\u544A\u7EC4\uFF08destination=ON_AD\uFF1B\u9ED8\u8BA4 LEAD_GENERATION\uFF09").requiredOption("--campaign-id <id>", "\u7CFB\u5217 ID").requiredOption("--page-id <id>", "\u6295\u653E\u4E3B\u9875 ID").requiredOption("--name <name>", "\u7EC4\u540D\u79F0").requiredOption("--countries <list>", "ISO \u56FD\u5BB6\uFF0C\u9017\u53F7\u5206\u9694\uFF08\u5982 US,GB\uFF09").option("--daily-budget <amount>", "\u65E5\u9884\u7B97\uFF0C\u4E3B\u5E01\u79CD\u5143\uFF08ABO\uFF09", parseFloat).option("--lifetime-budget <amount>", "\u603B\u9884\u7B97\uFF0C\u4E3B\u5E01\u79CD\u5143", parseFloat).option("--age-min <n>", "\u6700\u5C0F\u5E74\u9F84", parseInt).option("--age-max <n>", "\u6700\u5927\u5E74\u9F84", parseInt).option("--genders <list>", "1=\u7537,2=\u5973\uFF0C\u9017\u53F7\u5206\u9694").option("--optimization-goal <g>", "LEAD_GENERATION | QUALITY_LEAD").option("--bid-strategy <s>", "\u51FA\u4EF7\u7B56\u7565").option("--bid-amount <amount>", "\u51FA\u4EF7\u4E0A\u9650\uFF0C\u4E3B\u5E01\u79CD\u5143", parseFloat).option("--start-time <iso>", "\u5F00\u59CB\u65F6\u95F4").option("--end-time <iso>", "\u7ED3\u675F\u65F6\u95F4").option("--status <status>", "ACTIVE | PAUSED")
124208
+ )
124209
+ ).action(
124210
+ async (opts) => {
124211
+ await runMetaAdAdsetCreate(opts);
124212
+ }
124213
+ );
124214
+ addCommon(
124215
+ addAccount(
124216
+ meta.command("adset-edit").description("\u6539\u5E7F\u544A\u7EC4\uFF08targeting \u6574\u4EFD\u66FF\u6362\uFF0C\u53EA\u8BA4\u56FD\u5BB6/\u5E74\u9F84/\u6027\u522B\uFF09").requiredOption("--id <adSetId>", "\u5E7F\u544A\u7EC4 ID").option("--name <name>", "\u65B0\u540D\u79F0").option("--daily-budget <amount>", "\u65E5\u9884\u7B97\uFF0C\u4E3B\u5E01\u79CD\u5143", parseFloat).option("--lifetime-budget <amount>", "\u603B\u9884\u7B97\uFF0C\u4E3B\u5E01\u79CD\u5143", parseFloat).option("--countries <list>", "ISO \u56FD\u5BB6\uFF0C\u9017\u53F7\u5206\u9694").option("--age-min <n>", "\u6700\u5C0F\u5E74\u9F84", parseInt).option("--age-max <n>", "\u6700\u5927\u5E74\u9F84", parseInt).option("--genders <list>", "1=\u7537,2=\u5973").option("--optimization-goal <g>", "LEAD_GENERATION | QUALITY_LEAD").option("--bid-strategy <s>", "\u51FA\u4EF7\u7B56\u7565").option("--bid-amount <amount>", "\u51FA\u4EF7\u4E0A\u9650\uFF0C\u4E3B\u5E01\u79CD\u5143", parseFloat).option("--start-time <iso>", "\u5F00\u59CB\u65F6\u95F4").option("--end-time <iso>", "\u7ED3\u675F\u65F6\u95F4").option("--status <status>", "\u72B6\u6001")
124217
+ )
124218
+ ).action(
124219
+ async (opts) => {
124220
+ await runMetaAdAdsetEdit(opts);
124221
+ }
124222
+ );
124223
+ addCommon(
124224
+ addAccount(
124225
+ meta.command("adset-status").description("\u6539\u5E7F\u544A\u7EC4\u72B6\u6001").requiredOption("--id <adSetId>", "\u5E7F\u544A\u7EC4 ID").requiredOption("--status <status>", "\u76EE\u6807\u72B6\u6001")
124226
+ )
124227
+ ).action(
124228
+ async (opts) => {
124229
+ await runMetaAdAdsetStatus(opts);
124230
+ }
124231
+ );
124232
+ addCommon(
124233
+ addAccount(
124234
+ meta.command("creative-create").description("\u5EFA\u7EBF\u7D22\u521B\u610F\uFF08\u6302 Instant Form + image hash\uFF09").requiredOption("--page-id <id>", "\u4E3B\u9875 ID").requiredOption("--name <name>", "\u521B\u610F\u540D\u79F0").requiredOption("--message <text>", "\u6B63\u6587").requiredOption("--link <url>", "\u94FE\u63A5\uFF08\u5E38\u7528\u9690\u79C1\u653F\u7B56 URL\uFF09").requiredOption("--image-hash <hash>", "image-upload \u8FD4\u56DE\u7684 hash").requiredOption("--form-id <id>", "Instant Form ID").option("--headline <text>", "\u6807\u9898").option("--cta-type <type>", "CTA\uFF0C\u7F3A\u7701 SIGN_UP")
124235
+ )
124236
+ ).action(
124237
+ async (opts) => {
124238
+ await runMetaAdCreativeCreate(opts);
124239
+ }
124240
+ );
124241
+ addCommon(
124242
+ addAccount(
124243
+ meta.command("ad").description("\u6309 ID \u8BFB\u5E7F\u544A").requiredOption("--id <adId>", "\u5E7F\u544A ID")
124244
+ )
124245
+ ).action(
124246
+ async (opts) => {
124247
+ await runMetaAdGet(opts);
124248
+ }
124249
+ );
124250
+ addCommon(
124251
+ addAccount(
124252
+ meta.command("ad-create").description("\u5EFA\u5E7F\u544A\uFF08\u6302\u5DF2\u6709\u521B\u610F\uFF09").requiredOption("--adset-id <id>", "\u5E7F\u544A\u7EC4 ID").requiredOption("--creative-id <id>", "\u521B\u610F ID").requiredOption("--name <name>", "\u5E7F\u544A\u540D\u79F0").option("--status <status>", "ACTIVE | PAUSED")
124253
+ )
124254
+ ).action(
124255
+ async (opts) => {
124256
+ await runMetaAdCreate2(opts);
124257
+ }
124258
+ );
124259
+ addCommon(
124260
+ addAccount(
124261
+ meta.command("ad-edit").description("\u6539\u5E7F\u544A\uFF08\u540D\u79F0 / \u6362\u521B\u610F / \u72B6\u6001\uFF09").requiredOption("--id <adId>", "\u5E7F\u544A ID").option("--name <name>", "\u65B0\u540D\u79F0").option("--creative-id <id>", "\u65B0\u521B\u610F ID").option("--status <status>", "\u72B6\u6001")
124262
+ )
124263
+ ).action(
124264
+ async (opts) => {
124265
+ await runMetaAdEdit(opts);
124266
+ }
124267
+ );
124268
+ addCommon(
124269
+ addAccount(
124270
+ meta.command("ad-status").description("\u6539\u5E7F\u544A\u72B6\u6001").requiredOption("--id <adId>", "\u5E7F\u544A ID").requiredOption("--status <status>", "\u76EE\u6807\u72B6\u6001")
124271
+ )
124272
+ ).action(
124273
+ async (opts) => {
124274
+ await runMetaAdStatus(opts);
124275
+ }
124276
+ );
124277
+ }
124278
+
122323
124279
  // src/utils/cli-json-out-option.ts
122324
124280
  var JSON_OUT_DESC = "\u843D\u76D8 JSON + outline + cli-manifest[-<\u67E5\u8BE2id>].json\uFF1Bstdout \u4E00\u884C\u6458\u8981 JSON\uFF08\u542B outlineFile\uFF09";
122325
124281
  function attachJsonOutOption(cmd) {
@@ -122788,7 +124744,7 @@ function attachBaseCliOptions(cmd) {
122788
124744
  function splitKeywordSeeds(raw) {
122789
124745
  return raw.split(",").map((s) => s.trim()).filter(Boolean);
122790
124746
  }
122791
- function register22(program2) {
124747
+ function register23(program2) {
122792
124748
  const keywordCmd = program2.command("keyword").description(
122793
124749
  "Google \u5173\u952E\u5B57\u63A8\u8350\uFF08\u53EF\u9009 -a \u8D26\u6237 ID \u6309\u8D26\u6237\u5E01\u79CD\u51FA\u4EF7\uFF1B\u5426\u5219\u5171\u4EAB MCC/USD\uFF1B\u53EF --geo \u9650\u5B9A\u5E02\u573A\uFF09"
122794
124750
  );
@@ -123257,7 +125213,7 @@ async function runRagList(options) {
123257
125213
  ];
123258
125214
  console.log(lines.join("\n"));
123259
125215
  }
123260
- function register23(program2) {
125216
+ function register24(program2) {
123261
125217
  const ragCmd = program2.command("rag").description(
123262
125218
  "RAG \u77E5\u8BC6\u5E93\u68C0\u7D22\uFF08\u534F\u52A9\u5E7F\u544A\u6295\u653E\u3001\u62D3\u8BCD\u3001\u8D26\u6237\u8BCA\u65AD\u4E0E\u62A5\u544A\uFF1B\u9ED8\u8BA4 belongToId=account/me \u7684 companyId\uFF09"
123263
125219
  );
@@ -124705,7 +126661,7 @@ async function runAccountBmBind(opts) {
124705
126661
  }
124706
126662
 
124707
126663
  // src/commands/account-manage-register.ts
124708
- function register24(program2) {
126664
+ function register25(program2) {
124709
126665
  const accountCmd = program2.command("account").description("\u5E7F\u544A\u8D26\u6237\u7BA1\u7406\uFF1AOAuth \u6DFB\u52A0\u6388\u6743\u3001\u89E3\u9664\u5173\u8054\uFF08\u89E3\u7ED1\uFF09\u3001Google MCC \u7ED1\u5B9A/\u89E3\u7ED1\u3001\u8D26\u53F7\u5206\u4EAB");
124710
126666
  accountCmd.command("list", { hidden: true }).description("\uFF08\u517C\u5BB9\u522B\u540D\uFF09\u540C list-accounts").option(
124711
126667
  "-m, --media <type>",
@@ -126662,7 +128618,7 @@ async function runOpenAccountMeta(opts) {
126662
128618
  }
126663
128619
 
126664
128620
  // src/commands/open-account-register.ts
126665
- function register25(program2) {
128621
+ function register26(program2) {
126666
128622
  const openAccountCmd = program2.command("open-account").description("\u5F00\u6237\u7533\u8BF7\uFF08Google / TikTok / Yandex / Bing / Kwai / Meta \u5F00\u6237\u94FE\u63A5\uFF09");
126667
128623
  openAccountCmd.command("meta").description(
126668
128624
  "\u83B7\u53D6 Meta/Facebook \u5B98\u65B9\u5F00\u6237\u94FE\u63A5\uFF08GET MetaAccount/Management/GetOpenAccountLink\uFF0C\u4E0E\u7F51\u9875\u300C\u7533\u8BF7\u5F00\u6237\u300D\u4E00\u81F4\uFF09"
@@ -126910,7 +128866,7 @@ function register25(program2) {
126910
128866
  }
126911
128867
 
126912
128868
  // src/commands/diagnostic.ts
126913
- function register26(program2) {
128869
+ function register27(program2) {
126914
128870
  program2.command("diagnostic").description("\u8BCA\u65AD\u5165\u53E3\u8BF4\u660E\uFF1AGoogle \u5E7F\u544A\u8D26\u6237\u8BCA\u65AD\u9875 vs \u7F51\u7AD9\u8BCA\u65AD CLI").action(() => {
126915
128871
  console.log("\n\u8BCA\u65AD\u80FD\u529B\u8BF4\u660E\n");
126916
128872
  console.log(" \u3010Google \u5E7F\u544A\u8D26\u6237\u8BCA\u65AD\u3011");
@@ -127647,12 +129603,12 @@ var TEMPLATE_BASENAMES = {
127647
129603
  html: "website-diagnosis-report.html",
127648
129604
  runtime: "website-diagnosis-report.runtime.js"
127649
129605
  };
127650
- function resolveSkillTemplatePath(basename11) {
129606
+ function resolveSkillTemplatePath(basename13) {
127651
129607
  const dir = path19.dirname(fileURLToPath4(import.meta.url));
127652
129608
  const candidates = [
127653
- path19.join(dir, "skill", "report-templates", basename11),
127654
- path19.join(dir, "skill", "siluzan-ads", "report-templates", basename11),
127655
- path19.join(dir, "..", "..", "assets", "siluzan-ads", "report-templates", basename11)
129609
+ path19.join(dir, "skill", "report-templates", basename13),
129610
+ path19.join(dir, "skill", "siluzan-ads", "report-templates", basename13),
129611
+ path19.join(dir, "..", "..", "assets", "siluzan-ads", "report-templates", basename13)
127656
129612
  ];
127657
129613
  for (const p of candidates) {
127658
129614
  if (fs14.existsSync(p)) return p;
@@ -128526,12 +130482,12 @@ function buildGoogleAdsDiagnosisNarrativeChecks() {
128526
130482
  return checks;
128527
130483
  }
128528
130484
  var GOOGLE_ADS_DIAGNOSIS_NARRATIVE_CHECKS = buildGoogleAdsDiagnosisNarrativeChecks();
128529
- function resolveSkillTemplatePath2(basename11) {
130485
+ function resolveSkillTemplatePath2(basename13) {
128530
130486
  const dir = path21.dirname(fileURLToPath5(import.meta.url));
128531
130487
  const candidates = [
128532
- path21.join(dir, "skill", "report-templates", basename11),
128533
- path21.join(dir, "skill", "siluzan-ads", "report-templates", basename11),
128534
- path21.join(dir, "..", "..", "assets", "siluzan-ads", "report-templates", basename11)
130488
+ path21.join(dir, "skill", "report-templates", basename13),
130489
+ path21.join(dir, "skill", "siluzan-ads", "report-templates", basename13),
130490
+ path21.join(dir, "..", "..", "assets", "siluzan-ads", "report-templates", basename13)
128535
130491
  ];
128536
130492
  for (const p of candidates) {
128537
130493
  if (fs16.existsSync(p)) return p;
@@ -131262,12 +133218,12 @@ var TEMPLATE_BASENAMES2 = {
131262
133218
  runtime: "meta-period-report.runtime.js"
131263
133219
  };
131264
133220
  var INJECT_MARKER2 = "window.__META_PERIOD_REPORT__ = window.__META_PERIOD_REPORT__ || null;";
131265
- function resolveSkillTemplatePath4(basename11) {
133221
+ function resolveSkillTemplatePath4(basename13) {
131266
133222
  const dir = path33.dirname(fileURLToPath7(import.meta.url));
131267
133223
  const candidates = [
131268
- path33.join(dir, "skill", "report-templates", basename11),
131269
- path33.join(dir, "skill", "siluzan-ads", "report-templates", basename11),
131270
- path33.join(dir, "..", "..", "assets", "siluzan-ads", "report-templates", basename11)
133224
+ path33.join(dir, "skill", "report-templates", basename13),
133225
+ path33.join(dir, "skill", "siluzan-ads", "report-templates", basename13),
133226
+ path33.join(dir, "..", "..", "assets", "siluzan-ads", "report-templates", basename13)
131271
133227
  ];
131272
133228
  for (const p of candidates) {
131273
133229
  if (fs26.existsSync(p)) return p;
@@ -131714,12 +133670,12 @@ function formatMetaDiagnosisReportErrors(result) {
131714
133670
  // src/commands/facebook-analysis/diagnosis-render-report.ts
131715
133671
  var TEMPLATE_BASENAME2 = "meta-account-diagnosis-report.html";
131716
133672
  var INJECT_MARKER3 = "window.__META_DIAGNOSIS_REPORT__ = window.__META_DIAGNOSIS_REPORT__ || null;";
131717
- function resolveSkillTemplatePath5(basename11) {
133673
+ function resolveSkillTemplatePath5(basename13) {
131718
133674
  const dir = path35.dirname(fileURLToPath8(import.meta.url));
131719
133675
  const candidates = [
131720
- path35.join(dir, "skill", "report-templates", basename11),
131721
- path35.join(dir, "skill", "siluzan-ads", "report-templates", basename11),
131722
- path35.join(dir, "..", "..", "assets", "siluzan-ads", "report-templates", basename11)
133676
+ path35.join(dir, "skill", "report-templates", basename13),
133677
+ path35.join(dir, "skill", "siluzan-ads", "report-templates", basename13),
133678
+ path35.join(dir, "..", "..", "assets", "siluzan-ads", "report-templates", basename13)
131723
133679
  ];
131724
133680
  for (const p of candidates) {
131725
133681
  if (fs28.existsSync(p)) return p;
@@ -133242,12 +135198,12 @@ function formatBingPeriodReportErrors(result) {
133242
135198
  // src/commands/bing-analysis/render-report.ts
133243
135199
  var TEMPLATE_BASENAME3 = "bing-period-report.html";
133244
135200
  var INJECT_MARKER4 = "window.__BING_PERIOD_REPORT__ = window.__BING_PERIOD_REPORT__ || null;";
133245
- function resolveSkillTemplatePath6(basename11) {
135201
+ function resolveSkillTemplatePath6(basename13) {
133246
135202
  const dir = path39.dirname(fileURLToPath9(import.meta.url));
133247
135203
  const candidates = [
133248
- path39.join(dir, "skill", "report-templates", basename11),
133249
- path39.join(dir, "skill", "siluzan-ads", "report-templates", basename11),
133250
- path39.join(dir, "..", "..", "assets", "siluzan-ads", "report-templates", basename11)
135204
+ path39.join(dir, "skill", "report-templates", basename13),
135205
+ path39.join(dir, "skill", "siluzan-ads", "report-templates", basename13),
135206
+ path39.join(dir, "..", "..", "assets", "siluzan-ads", "report-templates", basename13)
133251
135207
  ];
133252
135208
  for (const p of candidates) {
133253
135209
  if (fs30.existsSync(p)) return p;
@@ -134139,12 +136095,12 @@ function formatTikTokPeriodReportErrors(result) {
134139
136095
  // src/commands/tiktok-analysis/render-report.ts
134140
136096
  var TEMPLATE_BASENAME4 = "tiktok-period-report.html";
134141
136097
  var INJECT_MARKER5 = "window.__TIKTOK_PERIOD_REPORT__ = window.__TIKTOK_PERIOD_REPORT__ || null;";
134142
- function resolveSkillTemplatePath7(basename11) {
136098
+ function resolveSkillTemplatePath7(basename13) {
134143
136099
  const dir = path42.dirname(fileURLToPath10(import.meta.url));
134144
136100
  const candidates = [
134145
- path42.join(dir, "skill", "report-templates", basename11),
134146
- path42.join(dir, "skill", "siluzan-ads", "report-templates", basename11),
134147
- path42.join(dir, "..", "..", "assets", "siluzan-ads", "report-templates", basename11)
136101
+ path42.join(dir, "skill", "report-templates", basename13),
136102
+ path42.join(dir, "skill", "siluzan-ads", "report-templates", basename13),
136103
+ path42.join(dir, "..", "..", "assets", "siluzan-ads", "report-templates", basename13)
134148
136104
  ];
134149
136105
  for (const p of candidates) {
134150
136106
  if (fs32.existsSync(p)) return p;
@@ -135913,12 +137869,12 @@ function formatYandexPeriodReportErrors(result) {
135913
137869
  // src/commands/yandex-analysis/render-report.ts
135914
137870
  var TEMPLATE_BASENAME5 = "yandex-period-report.html";
135915
137871
  var INJECT_MARKER6 = "window.__YANDEX_PERIOD_REPORT__ = window.__YANDEX_PERIOD_REPORT__ || null;";
135916
- function resolveSkillTemplatePath8(basename11) {
137872
+ function resolveSkillTemplatePath8(basename13) {
135917
137873
  const dir = path45.dirname(fileURLToPath11(import.meta.url));
135918
137874
  const candidates = [
135919
- path45.join(dir, "skill", "report-templates", basename11),
135920
- path45.join(dir, "skill", "siluzan-ads", "report-templates", basename11),
135921
- path45.join(dir, "..", "..", "assets", "siluzan-ads", "report-templates", basename11)
137875
+ path45.join(dir, "skill", "report-templates", basename13),
137876
+ path45.join(dir, "skill", "siluzan-ads", "report-templates", basename13),
137877
+ path45.join(dir, "..", "..", "assets", "siluzan-ads", "report-templates", basename13)
135922
137878
  ];
135923
137879
  for (const p of candidates) {
135924
137880
  if (fs34.existsSync(p)) return p;
@@ -136196,6 +138152,7 @@ var REGISTRARS = [
136196
138152
  register24,
136197
138153
  register25,
136198
138154
  register26,
138155
+ register27,
136199
138156
  registerWebsiteDiagnosisCommands,
136200
138157
  registerMarketAnalysisCommands,
136201
138158
  registerGoogleAdsDiagnosisCommands