siluzan-tso-cli 1.1.58 → 1.1.59-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.
package/dist/index.js CHANGED
@@ -4049,7 +4049,7 @@ var DEFAULT_API_BASE;
4049
4049
  var init_defaults = __esm({
4050
4050
  "src/config/defaults.ts"() {
4051
4051
  "use strict";
4052
- DEFAULT_API_BASE = "https://tso-api.siluzan.com";
4052
+ DEFAULT_API_BASE = "https://tso-api-ci.siluzan.com";
4053
4053
  }
4054
4054
  });
4055
4055
 
@@ -5983,6 +5983,7 @@ async function runAccountsDigest(opts) {
5983
5983
  const fields = (row.spend ?? 0) > 0 ? summaries.get(row.mediaCustomerId) ?? errorCampaignTypes("\u672A\u8FD4\u56DE\u8BE5\u8D26\u6237\u7684\u7CFB\u5217\u7C7B\u578B") : skippedNoSpendCampaignTypes();
5984
5984
  row.campaignTypesStatus = fields.campaignTypesStatus;
5985
5985
  row.campaignTypes = fields.campaignTypes;
5986
+ row.channelTypeV2 = fields.campaignTypesStatus === "ok" ? fields.campaignTypes : null;
5986
5987
  row.hasDisplay = fields.hasDisplay;
5987
5988
  row.hasPmax = fields.hasPmax;
5988
5989
  row.campaignTypesError = fields.campaignTypesError;
@@ -6726,8 +6727,8 @@ var init_bid_modifier_display = __esm({
6726
6727
  });
6727
6728
 
6728
6729
  // src/commands/facebook-analysis/xlsx-style-engine.ts
6729
- import fs10 from "fs";
6730
- import path15 from "path";
6730
+ import fs11 from "fs";
6731
+ import path16 from "path";
6731
6732
  function resolveAccent(accent) {
6732
6733
  if (!accent) return PLATFORM_ACCENTS.default;
6733
6734
  const hex = accent.replace(/^#/, "");
@@ -7216,9 +7217,9 @@ var init_xlsx_style_engine = __esm({
7216
7217
  return zipStore(files);
7217
7218
  }
7218
7219
  writeFile(filePath) {
7219
- const abs = path15.resolve(filePath);
7220
- fs10.mkdirSync(path15.dirname(abs), { recursive: true });
7221
- fs10.writeFileSync(abs, this.toBuffer());
7220
+ const abs = path16.resolve(filePath);
7221
+ fs11.mkdirSync(path16.dirname(abs), { recursive: true });
7222
+ fs11.writeFileSync(abs, this.toBuffer());
7222
7223
  return abs;
7223
7224
  }
7224
7225
  };
@@ -103820,8 +103821,8 @@ var init_okki_display = __esm({
103820
103821
  });
103821
103822
 
103822
103823
  // src/commands/google-analysis/merge-snapshot.ts
103823
- import * as fs27 from "fs/promises";
103824
- import * as path32 from "path";
103824
+ import * as fs28 from "fs/promises";
103825
+ import * as path33 from "path";
103825
103826
  function asRecord20(value) {
103826
103827
  if (value && typeof value === "object" && !Array.isArray(value)) {
103827
103828
  return value;
@@ -103839,7 +103840,7 @@ async function readManifest(snapshotDir, accountId) {
103839
103840
  const candidates = [];
103840
103841
  if (accountId) candidates.push(googleAnalysisManifestFile(accountId));
103841
103842
  try {
103842
- const entries = await fs27.readdir(snapshotDir);
103843
+ const entries = await fs28.readdir(snapshotDir);
103843
103844
  const manifestNames = entries.filter((n) => n.startsWith("manifest") && n.endsWith(".json"));
103844
103845
  for (const name2 of manifestNames) {
103845
103846
  if (!candidates.includes(name2)) candidates.push(name2);
@@ -103848,7 +103849,7 @@ async function readManifest(snapshotDir, accountId) {
103848
103849
  }
103849
103850
  for (const name2 of candidates) {
103850
103851
  try {
103851
- const raw = await fs27.readFile(path32.join(snapshotDir, name2), "utf8");
103852
+ const raw = await fs28.readFile(path33.join(snapshotDir, name2), "utf8");
103852
103853
  const parsed = JSON.parse(raw);
103853
103854
  if (Array.isArray(parsed.artifacts) && parsed.artifacts.length > 0) return parsed;
103854
103855
  } catch {
@@ -103858,7 +103859,7 @@ async function readManifest(snapshotDir, accountId) {
103858
103859
  }
103859
103860
  async function loadSectionFile(snapshotDir, file) {
103860
103861
  try {
103861
- const raw = await fs27.readFile(path32.join(snapshotDir, file), "utf8");
103862
+ const raw = await fs28.readFile(path33.join(snapshotDir, file), "utf8");
103862
103863
  const parsed = JSON.parse(raw);
103863
103864
  if (parsed && typeof parsed === "object") return parsed;
103864
103865
  return null;
@@ -103892,7 +103893,7 @@ async function recoverMissingReportSections(sectionMap, absDir, accountId, prefe
103892
103893
  if (!accountId) return;
103893
103894
  let entries;
103894
103895
  try {
103895
- entries = await fs27.readdir(absDir);
103896
+ entries = await fs28.readdir(absDir);
103896
103897
  } catch {
103897
103898
  return;
103898
103899
  }
@@ -103964,7 +103965,7 @@ function sumCampaignRowsToPeriodKpis(rows) {
103964
103965
  };
103965
103966
  }
103966
103967
  async function mergeGoogleAnalysisSnapshotIntoReport(payload, snapshotDir) {
103967
- const absDir = path32.resolve(snapshotDir);
103968
+ const absDir = path33.resolve(snapshotDir);
103968
103969
  const metaAccountId = typeof asRecord20(payload.meta)?.accountId === "string" ? asRecord20(payload.meta)?.accountId : void 0;
103969
103970
  const manifest = await readManifest(snapshotDir, metaAccountId);
103970
103971
  if (!manifest) {
@@ -104310,8 +104311,8 @@ var init_okki_report_content = __esm({
104310
104311
  });
104311
104312
 
104312
104313
  // src/commands/google-analysis/okki-merge-snapshot.ts
104313
- import * as fs28 from "fs/promises";
104314
- import * as path33 from "path";
104314
+ import * as fs29 from "fs/promises";
104315
+ import * as path34 from "path";
104315
104316
  function num5(value) {
104316
104317
  const n = Number(value);
104317
104318
  return Number.isFinite(n) ? n : void 0;
@@ -104337,7 +104338,7 @@ async function readManifest2(snapshotDir, accountId) {
104337
104338
  const candidates = [];
104338
104339
  if (accountId) candidates.push(googleAnalysisManifestFile(accountId));
104339
104340
  try {
104340
- const entries = await fs28.readdir(snapshotDir);
104341
+ const entries = await fs29.readdir(snapshotDir);
104341
104342
  for (const name2 of entries.filter((n) => n.startsWith("manifest") && n.endsWith(".json"))) {
104342
104343
  if (!candidates.includes(name2)) candidates.push(name2);
104343
104344
  }
@@ -104345,7 +104346,7 @@ async function readManifest2(snapshotDir, accountId) {
104345
104346
  }
104346
104347
  for (const name2 of candidates) {
104347
104348
  try {
104348
- const raw = await fs28.readFile(path33.join(snapshotDir, name2), "utf8");
104349
+ const raw = await fs29.readFile(path34.join(snapshotDir, name2), "utf8");
104349
104350
  const parsed = JSON.parse(raw);
104350
104351
  if (Array.isArray(parsed.artifacts) && parsed.artifacts.length > 0) return parsed;
104351
104352
  } catch {
@@ -104355,7 +104356,7 @@ async function readManifest2(snapshotDir, accountId) {
104355
104356
  }
104356
104357
  async function loadSectionFile2(snapshotDir, file) {
104357
104358
  try {
104358
- const raw = await fs28.readFile(path33.join(snapshotDir, file), "utf8");
104359
+ const raw = await fs29.readFile(path34.join(snapshotDir, file), "utf8");
104359
104360
  const parsed = JSON.parse(raw);
104360
104361
  if (parsed && typeof parsed === "object") return parsed;
104361
104362
  return null;
@@ -104364,7 +104365,7 @@ async function loadSectionFile2(snapshotDir, file) {
104364
104365
  }
104365
104366
  }
104366
104367
  async function mergeOkkiSnapshotIntoReport(payload, snapshotDir) {
104367
- const absDir = path33.resolve(snapshotDir);
104368
+ const absDir = path34.resolve(snapshotDir);
104368
104369
  const metaAccountId = typeof asRecord21(payload.meta)?.accountId === "string" ? asRecord21(payload.meta)?.accountId : void 0;
104369
104370
  const manifest = await readManifest2(absDir, metaAccountId);
104370
104371
  if (!manifest) {
@@ -104381,7 +104382,7 @@ async function mergeOkkiSnapshotIntoReport(payload, snapshotDir) {
104381
104382
  if (accountId) {
104382
104383
  let entries = [];
104383
104384
  try {
104384
- entries = await fs28.readdir(absDir);
104385
+ entries = await fs29.readdir(absDir);
104385
104386
  } catch {
104386
104387
  entries = [];
104387
104388
  }
@@ -104875,10 +104876,10 @@ var init_okki_render_excel = __esm({
104875
104876
  });
104876
104877
 
104877
104878
  // src/commands/google-analysis/okki-render-report.ts
104878
- import fs29 from "fs/promises";
104879
- import path34 from "path";
104879
+ import fs30 from "fs/promises";
104880
+ import path35 from "path";
104880
104881
  async function readJsonFile2(filePath) {
104881
- const text3 = await fs29.readFile(filePath, "utf8");
104882
+ const text3 = await fs30.readFile(filePath, "utf8");
104882
104883
  try {
104883
104884
  return JSON.parse(text3);
104884
104885
  } catch {
@@ -104892,7 +104893,7 @@ function asPayload(value) {
104892
104893
  throw new Error("\u62A5\u544A\u6570\u636E\u987B\u4E3A JSON \u5BF9\u8C61");
104893
104894
  }
104894
104895
  async function runGoogleAnalysisOkkiRender(opts) {
104895
- const dataPath = path34.resolve(opts.dataFile);
104896
+ const dataPath = path35.resolve(opts.dataFile);
104896
104897
  const snapshotDir = opts.snapshotDir?.trim();
104897
104898
  if (!snapshotDir) {
104898
104899
  console.error("\n\u274C okki-render \u5FC5\u987B\u4F20 --snapshot-dir\uFF08google-analysis --json-out \u76EE\u5F55\uFF09\n");
@@ -104900,7 +104901,7 @@ async function runGoogleAnalysisOkkiRender(opts) {
104900
104901
  }
104901
104902
  let data = asPayload(await readJsonFile2(dataPath));
104902
104903
  try {
104903
- data = await mergeOkkiSnapshotIntoReport(data, path34.resolve(snapshotDir));
104904
+ data = await mergeOkkiSnapshotIntoReport(data, path35.resolve(snapshotDir));
104904
104905
  } catch (err) {
104905
104906
  console.error(`
104906
104907
  \u274C ${err instanceof Error ? err.message : String(err)}
@@ -104919,10 +104920,10 @@ async function runGoogleAnalysisOkkiRender(opts) {
104919
104920
  );
104920
104921
  process.exit(1);
104921
104922
  }
104922
- const outPath = path34.resolve(
104923
- opts.out ?? path34.join(path34.dirname(dataPath), "okki-weekly-report.xlsx")
104923
+ const outPath = path35.resolve(
104924
+ opts.out ?? path35.join(path35.dirname(dataPath), "okki-weekly-report.xlsx")
104924
104925
  );
104925
- await fs29.mkdir(path34.dirname(outPath), { recursive: true });
104926
+ await fs30.mkdir(path35.dirname(outPath), { recursive: true });
104926
104927
  writeOkkiWeeklyExcel(outPath, data);
104927
104928
  console.log(`
104928
104929
  \u2705 OKKI \u5468\u62A5\u5DF2\u751F\u6210\uFF1A${outPath}
@@ -105082,19 +105083,19 @@ var init_report_content = __esm({
105082
105083
  });
105083
105084
 
105084
105085
  // src/commands/google-analysis/render-report.ts
105085
- import fs30 from "fs";
105086
+ import fs31 from "fs";
105086
105087
  import fsPromises2 from "fs/promises";
105087
- import path35 from "path";
105088
+ import path36 from "path";
105088
105089
  import { fileURLToPath as fileURLToPath9 } from "url";
105089
105090
  function resolveSkillTemplatePath3(basename13) {
105090
- const dir = path35.dirname(fileURLToPath9(import.meta.url));
105091
+ const dir = path36.dirname(fileURLToPath9(import.meta.url));
105091
105092
  const candidates = [
105092
- path35.join(dir, "skill", "report-templates", basename13),
105093
- path35.join(dir, "skill", "siluzan-ads", "report-templates", basename13),
105094
- path35.join(dir, "..", "..", "assets", "siluzan-ads", "report-templates", basename13)
105093
+ path36.join(dir, "skill", "report-templates", basename13),
105094
+ path36.join(dir, "skill", "siluzan-ads", "report-templates", basename13),
105095
+ path36.join(dir, "..", "..", "assets", "siluzan-ads", "report-templates", basename13)
105095
105096
  ];
105096
105097
  for (const p of candidates) {
105097
- if (fs30.existsSync(p)) return p;
105098
+ if (fs31.existsSync(p)) return p;
105098
105099
  }
105099
105100
  return candidates[0];
105100
105101
  }
@@ -105123,10 +105124,10 @@ function injectReportData2(html, payload) {
105123
105124
  return html.replace(INJECT_MARKER, `window.__GOOGLE_PERIOD_REPORT__ = ${json};`);
105124
105125
  }
105125
105126
  async function runGoogleAnalysisRender(opts) {
105126
- const dataPath = path35.resolve(opts.dataFile);
105127
+ const dataPath = path36.resolve(opts.dataFile);
105127
105128
  let data = asRecord23(await readJsonFile3(dataPath));
105128
105129
  if (opts.snapshotDir?.trim()) {
105129
- data = await mergeGoogleAnalysisSnapshotIntoReport(data, path35.resolve(opts.snapshotDir));
105130
+ data = await mergeGoogleAnalysisSnapshotIntoReport(data, path36.resolve(opts.snapshotDir));
105130
105131
  }
105131
105132
  const contentCheck = validateGooglePeriodReportContent(data);
105132
105133
  if (!contentCheck.ok) {
@@ -105152,10 +105153,10 @@ async function runGoogleAnalysisRender(opts) {
105152
105153
  `);
105153
105154
  process.exit(1);
105154
105155
  }
105155
- const outPath = path35.resolve(
105156
- opts.out ?? path35.join(path35.dirname(dataPath), "google-period-report.html")
105156
+ const outPath = path36.resolve(
105157
+ opts.out ?? path36.join(path36.dirname(dataPath), "google-period-report.html")
105157
105158
  );
105158
- const outDir = path35.dirname(outPath);
105159
+ const outDir = path36.dirname(outPath);
105159
105160
  await fsPromises2.mkdir(outDir, { recursive: true });
105160
105161
  await fsPromises2.writeFile(outPath, injectReportData2(html, data), "utf8");
105161
105162
  const { stats } = contentCheck;
@@ -105184,8 +105185,8 @@ var init_render_report = __esm({
105184
105185
  });
105185
105186
 
105186
105187
  // src/utils/batch-manifest.ts
105187
- import * as fs31 from "fs/promises";
105188
- import * as path36 from "path";
105188
+ import * as fs32 from "fs/promises";
105189
+ import * as path37 from "path";
105189
105190
  import { randomUUID as randomUUID2 } from "crypto";
105190
105191
  function generateRunId(now = /* @__PURE__ */ new Date()) {
105191
105192
  const pad3 = (n) => String(n).padStart(2, "0");
@@ -105199,26 +105200,26 @@ function isValidRunId(id) {
105199
105200
  }
105200
105201
  function resolveBatchPaths(baseDir, runId) {
105201
105202
  if (!isValidRunId(runId)) throw new Error(`\u975E\u6CD5 runId\uFF1A${runId}`);
105202
- const runDir = path36.resolve(baseDir, runId);
105203
- const stateDir = path36.join(runDir, "state");
105203
+ const runDir = path37.resolve(baseDir, runId);
105204
+ const stateDir = path37.join(runDir, "state");
105204
105205
  return {
105205
105206
  runDir,
105206
- runManifestFile: path36.join(runDir, "run-manifest.json"),
105207
- progressFile: path36.join(runDir, "progress.json"),
105208
- accountsFile: path36.join(runDir, "accounts.json"),
105209
- resultsDir: path36.join(runDir, "results"),
105210
- errorsDir: path36.join(runDir, "errors"),
105207
+ runManifestFile: path37.join(runDir, "run-manifest.json"),
105208
+ progressFile: path37.join(runDir, "progress.json"),
105209
+ accountsFile: path37.join(runDir, "accounts.json"),
105210
+ resultsDir: path37.join(runDir, "results"),
105211
+ errorsDir: path37.join(runDir, "errors"),
105211
105212
  stateDir,
105212
- tasksLogFile: path36.join(stateDir, "tasks.jsonl"),
105213
- lockFile: path36.join(stateDir, "tasks.lock")
105213
+ tasksLogFile: path37.join(stateDir, "tasks.jsonl"),
105214
+ lockFile: path37.join(stateDir, "tasks.lock")
105214
105215
  };
105215
105216
  }
105216
105217
  async function ensureAccountDirs(paths, accountId) {
105217
105218
  const safe = sanitizeAccountSegment(accountId);
105218
- const results = path36.join(paths.resultsDir, safe);
105219
- const errors = path36.join(paths.errorsDir, safe);
105220
- await fs31.mkdir(results, { recursive: true });
105221
- await fs31.mkdir(errors, { recursive: true });
105219
+ const results = path37.join(paths.resultsDir, safe);
105220
+ const errors = path37.join(paths.errorsDir, safe);
105221
+ await fs32.mkdir(results, { recursive: true });
105222
+ await fs32.mkdir(errors, { recursive: true });
105222
105223
  return { results, errors };
105223
105224
  }
105224
105225
  function sanitizeAccountSegment(accountId) {
@@ -105233,20 +105234,20 @@ function sanitizeAccountSegment(accountId) {
105233
105234
  return slug;
105234
105235
  }
105235
105236
  async function atomicWriteFile(target, content) {
105236
- const dir = path36.dirname(target);
105237
- await fs31.mkdir(dir, { recursive: true });
105238
- const tmp = path36.join(dir, `.tmp-${randomUUID2()}-${path36.basename(target)}`);
105237
+ const dir = path37.dirname(target);
105238
+ await fs32.mkdir(dir, { recursive: true });
105239
+ const tmp = path37.join(dir, `.tmp-${randomUUID2()}-${path37.basename(target)}`);
105239
105240
  try {
105240
- await fs31.writeFile(tmp, content, "utf8");
105241
- await fs31.rename(tmp, target);
105241
+ await fs32.writeFile(tmp, content, "utf8");
105242
+ await fs32.rename(tmp, target);
105242
105243
  } catch (e) {
105243
- await fs31.rm(tmp, { force: true }).catch(() => void 0);
105244
+ await fs32.rm(tmp, { force: true }).catch(() => void 0);
105244
105245
  throw e;
105245
105246
  }
105246
105247
  }
105247
105248
  async function readJsonIfExists(file) {
105248
105249
  try {
105249
- const raw = await fs31.readFile(file, "utf8");
105250
+ const raw = await fs32.readFile(file, "utf8");
105250
105251
  return JSON.parse(raw);
105251
105252
  } catch (e) {
105252
105253
  if (e.code === "ENOENT") return null;
@@ -105289,8 +105290,8 @@ function isTaskLogEntry(value) {
105289
105290
  return typeof row.at === "string" && typeof row.id === "string" && typeof row.accountId === "string" && typeof row.section === "string" && typeof row.state === "string" && TASK_LOG_STATES.has(row.state) && typeof row.attempts === "number";
105290
105291
  }
105291
105292
  async function writeTaskLogLine(paths, entry) {
105292
- await fs31.mkdir(paths.stateDir, { recursive: true });
105293
- await fs31.appendFile(paths.tasksLogFile, JSON.stringify(clampTaskLogEntry(entry)) + "\n", "utf8");
105293
+ await fs32.mkdir(paths.stateDir, { recursive: true });
105294
+ await fs32.appendFile(paths.tasksLogFile, JSON.stringify(clampTaskLogEntry(entry)) + "\n", "utf8");
105294
105295
  }
105295
105296
  async function appendTaskLog(paths, entry) {
105296
105297
  const file = paths.tasksLogFile;
@@ -105308,7 +105309,7 @@ async function appendTaskLog(paths, entry) {
105308
105309
  async function readTaskLog(paths) {
105309
105310
  let raw;
105310
105311
  try {
105311
- raw = await fs31.readFile(paths.tasksLogFile, "utf8");
105312
+ raw = await fs32.readFile(paths.tasksLogFile, "utf8");
105312
105313
  } catch (e) {
105313
105314
  if (e.code === "ENOENT") return [];
105314
105315
  throw e;
@@ -105386,9 +105387,9 @@ function emptyProgress(runId, totalTasks, accountConcurrency, sectionConcurrency
105386
105387
  };
105387
105388
  }
105388
105389
  async function tryAcquireLock(paths) {
105389
- await fs31.mkdir(paths.stateDir, { recursive: true });
105390
+ await fs32.mkdir(paths.stateDir, { recursive: true });
105390
105391
  try {
105391
- const handle = await fs31.open(paths.lockFile, "wx");
105392
+ const handle = await fs32.open(paths.lockFile, "wx");
105392
105393
  await handle.writeFile(`${process.pid}@${(/* @__PURE__ */ new Date()).toISOString()}`);
105393
105394
  await handle.close();
105394
105395
  return true;
@@ -105398,7 +105399,7 @@ async function tryAcquireLock(paths) {
105398
105399
  }
105399
105400
  }
105400
105401
  async function releaseLock(paths) {
105401
- await fs31.rm(paths.lockFile, { force: true }).catch(() => void 0);
105402
+ await fs32.rm(paths.lockFile, { force: true }).catch(() => void 0);
105402
105403
  }
105403
105404
  var BATCH_SCHEMA_VERSION, MAX_TASK_LOG_ERROR_CHARS, TASK_LOG_STATES, taskLogAppendQueue;
105404
105405
  var init_batch_manifest = __esm({
@@ -105420,8 +105421,8 @@ var init_batch_manifest = __esm({
105420
105421
 
105421
105422
  // src/utils/batch-runner.ts
105422
105423
  import { performance as performance2 } from "perf_hooks";
105423
- import * as path37 from "path";
105424
- import * as fs32 from "fs/promises";
105424
+ import * as path38 from "path";
105425
+ import * as fs33 from "fs/promises";
105425
105426
  async function runPool(items3, concurrencyRef, worker, signal) {
105426
105427
  let cursor = 0;
105427
105428
  const launch = () => {
@@ -105560,7 +105561,7 @@ async function runBatch(opts) {
105560
105561
  }
105561
105562
  const inner = {
105562
105563
  ...emptyProgress(
105563
- opts.paths.runDir.split(path37.sep).pop() ?? "run",
105564
+ opts.paths.runDir.split(path38.sep).pop() ?? "run",
105564
105565
  allTasks.length,
105565
105566
  opts.accountConcurrency,
105566
105567
  opts.sectionConcurrency
@@ -105685,7 +105686,7 @@ async function runBatch(opts) {
105685
105686
  signal: aborterToken.signal
105686
105687
  });
105687
105688
  await writer({
105688
- snapshotDir: path37.join(opts.paths.resultsDir, sanitizeAccountSegment(task.accountId)),
105689
+ snapshotDir: path38.join(opts.paths.resultsDir, sanitizeAccountSegment(task.accountId)),
105689
105690
  section: task.section,
105690
105691
  accountId: task.accountId,
105691
105692
  dateRange: result.dateRange,
@@ -105774,7 +105775,7 @@ async function runBatch(opts) {
105774
105775
  };
105775
105776
  }
105776
105777
  async function writeTaskError(paths, errorsDir, task, err, cls) {
105777
- const file = path37.join(errorsDir, `${sanitizeSection(task.section)}.error.json`);
105778
+ const file = path38.join(errorsDir, `${sanitizeSection(task.section)}.error.json`);
105778
105779
  const payload = {
105779
105780
  schemaVersion: 1,
105780
105781
  taskId: task.id,
@@ -105820,10 +105821,10 @@ async function persistRunMetadata(input) {
105820
105821
  accountsTotal: input.accounts.length,
105821
105822
  invocation: input.invocation
105822
105823
  };
105823
- await fs32.mkdir(paths.runDir, { recursive: true });
105824
- await fs32.mkdir(paths.stateDir, { recursive: true });
105825
- await fs32.mkdir(paths.resultsDir, { recursive: true });
105826
- await fs32.mkdir(paths.errorsDir, { recursive: true });
105824
+ await fs33.mkdir(paths.runDir, { recursive: true });
105825
+ await fs33.mkdir(paths.stateDir, { recursive: true });
105826
+ await fs33.mkdir(paths.resultsDir, { recursive: true });
105827
+ await fs33.mkdir(paths.errorsDir, { recursive: true });
105827
105828
  await writeRunManifest(paths, manifest);
105828
105829
  await writeAccounts(paths, input.accounts);
105829
105830
  return paths;
@@ -105886,7 +105887,7 @@ __export(google_analysis_batch_exports, {
105886
105887
  parseAccountIds: () => parseAccountIds,
105887
105888
  registerGoogleAnalysisBatchCommands: () => registerGoogleAnalysisBatchCommands
105888
105889
  });
105889
- import * as path38 from "path";
105890
+ import * as path39 from "path";
105890
105891
  import { performance as performance3 } from "perf_hooks";
105891
105892
  function parseSections(input) {
105892
105893
  if (!input || !input.trim()) return DEFAULT_SECTIONS;
@@ -106421,7 +106422,7 @@ var init_google_analysis_batch = __esm({
106421
106422
  });
106422
106423
 
106423
106424
  // src/commands/google-analysis/register-cli.ts
106424
- import * as path39 from "path";
106425
+ import * as path40 from "path";
106425
106426
  import { performance as performance4 } from "perf_hooks";
106426
106427
  import { Command } from "commander";
106427
106428
  function normalizeAnalysisDateOpts(opts) {
@@ -106559,7 +106560,7 @@ async function runAllSections(opts) {
106559
106560
  }
106560
106561
  const succeeded = results.filter((r) => r.ok).length;
106561
106562
  const failed = results.length - succeeded;
106562
- const absoluteSnapshotDir = path39.resolve(opts.jsonOut);
106563
+ const absoluteSnapshotDir = path40.resolve(opts.jsonOut);
106563
106564
  const summary = {
106564
106565
  kind: "siluzan-tso-google-analysis-snapshot-batch",
106565
106566
  absoluteSnapshotDir,
@@ -106826,8 +106827,8 @@ var init_google_analysis3 = __esm({
106826
106827
  // src/index.ts
106827
106828
  init_dist();
106828
106829
  init_dist();
106829
- import * as fs43 from "fs";
106830
- import * as path54 from "path";
106830
+ import * as fs44 from "fs";
106831
+ import * as path55 from "path";
106831
106832
  import { fileURLToPath as fileURLToPath15 } from "url";
106832
106833
  import { Command as Command6 } from "commander";
106833
106834
 
@@ -107317,8 +107318,8 @@ function deriveWebUrl(apiBaseUrl) {
107317
107318
  }
107318
107319
  var TSO_UMI_ROUTE_PREFIX = "/v3umijs/tso";
107319
107320
  function buildTsoPageWebUrl(webUrl, tsoSubRoute, extraQuery) {
107320
- const path55 = tsoSubRoute.replace(/^\/+/, "");
107321
- const umiPath = `${TSO_UMI_ROUTE_PREFIX}/${path55}`;
107321
+ const path56 = tsoSubRoute.replace(/^\/+/, "");
107322
+ const umiPath = `${TSO_UMI_ROUTE_PREFIX}/${path56}`;
107322
107323
  const q = new URLSearchParams({ tso: umiPath });
107323
107324
  if (extraQuery) {
107324
107325
  for (const [key, value] of Object.entries(extraQuery)) {
@@ -107326,7 +107327,7 @@ function buildTsoPageWebUrl(webUrl, tsoSubRoute, extraQuery) {
107326
107327
  if (v) q.set(key, v);
107327
107328
  }
107328
107329
  }
107329
- return `${webUrl.replace(/\/+$/, "")}/v3/foreign_trade/tso/${path55}?${q.toString()}`;
107330
+ return `${webUrl.replace(/\/+$/, "")}/v3/foreign_trade/tso/${path56}?${q.toString()}`;
107330
107331
  }
107331
107332
  function cmdConfigShow() {
107332
107333
  const shared = readSharedConfig();
@@ -113865,6 +113866,78 @@ init_shared();
113865
113866
  // src/commands/ad/campaign.ts
113866
113867
  init_auth();
113867
113868
  init_cli_json_snapshot();
113869
+
113870
+ // src/commands/ad/digest-campaign-reuse.ts
113871
+ import fs9 from "fs";
113872
+ import path14 from "path";
113873
+ var MAX_DEPTH = 4;
113874
+ var MAX_FILES = 20;
113875
+ function normalizeAccountId(id) {
113876
+ return id.replace(/[-\s]/g, "");
113877
+ }
113878
+ function listDigestFiles(dir, depth, out) {
113879
+ if (depth > MAX_DEPTH || out.length >= MAX_FILES) return;
113880
+ let entries;
113881
+ try {
113882
+ entries = fs9.readdirSync(dir, { withFileTypes: true });
113883
+ } catch {
113884
+ return;
113885
+ }
113886
+ for (const entry of entries) {
113887
+ if (out.length >= MAX_FILES) return;
113888
+ if (entry.name === "node_modules" || entry.name.startsWith(".")) continue;
113889
+ const full = path14.join(dir, entry.name);
113890
+ if (entry.isDirectory()) {
113891
+ listDigestFiles(full, depth + 1, out);
113892
+ continue;
113893
+ }
113894
+ if (entry.isFile() && /^accounts-digest-.+\.json$/i.test(entry.name)) {
113895
+ out.push(full);
113896
+ }
113897
+ }
113898
+ }
113899
+ function readHit(file, wantedId) {
113900
+ let parsed;
113901
+ try {
113902
+ parsed = JSON.parse(fs9.readFileSync(file, "utf8"));
113903
+ } catch {
113904
+ return null;
113905
+ }
113906
+ if (!parsed || typeof parsed !== "object") return null;
113907
+ const items3 = parsed.data?.items;
113908
+ if (!Array.isArray(items3)) return null;
113909
+ for (const item of items3) {
113910
+ if (!item || typeof item !== "object") continue;
113911
+ const row = item;
113912
+ const id = normalizeAccountId(String(row.mediaCustomerId ?? ""));
113913
+ if (!id || id !== wantedId) continue;
113914
+ if (row.campaignTypesStatus !== "ok") continue;
113915
+ const campaignTypes = Array.isArray(row.campaignTypes) ? row.campaignTypes.map(String) : [];
113916
+ const channelTypeV2 = Array.isArray(row.channelTypeV2) ? row.channelTypeV2.map(String) : campaignTypes;
113917
+ return {
113918
+ mediaCustomerId: id,
113919
+ campaignTypes,
113920
+ channelTypeV2,
113921
+ hasDisplay: row.hasDisplay === true,
113922
+ hasPmax: row.hasPmax === true,
113923
+ sourceFile: file
113924
+ };
113925
+ }
113926
+ return null;
113927
+ }
113928
+ function findDigestCampaignTypes(cwd, accountId) {
113929
+ const wanted = normalizeAccountId(accountId);
113930
+ if (!wanted) return null;
113931
+ const files = [];
113932
+ listDigestFiles(cwd, 0, files);
113933
+ for (const file of files) {
113934
+ const hit = readHit(file, wanted);
113935
+ if (hit) return hit;
113936
+ }
113937
+ return null;
113938
+ }
113939
+
113940
+ // src/commands/ad/campaign.ts
113868
113941
  init_strip_legacy_google_fields();
113869
113942
  init_cli_table();
113870
113943
 
@@ -115280,7 +115353,7 @@ function cloneKeywordBlockShell(block) {
115280
115353
  delete shell["matchTypeV2"];
115281
115354
  return shell;
115282
115355
  }
115283
- function splitKeywordsForBatchJobBlockIfMixed(block, path55, warnings) {
115356
+ function splitKeywordsForBatchJobBlockIfMixed(block, path56, warnings) {
115284
115357
  const declaredUi = matchTypeV2ToUi(readBlockMatchTypeRaw(block));
115285
115358
  const entries = collectKeywordEntriesFromBlock(block);
115286
115359
  if (entries.length === 0) return [block];
@@ -115299,7 +115372,7 @@ function splitKeywordsForBatchJobBlockIfMixed(block, path55, warnings) {
115299
115372
  (ui) => matchTypeUiToV2(ui)
115300
115373
  );
115301
115374
  warnings.push(
115302
- `${path55} \u542B\u591A\u79CD\u5339\u914D\u7C7B\u578B\uFF0C\u5DF2\u81EA\u52A8\u62C6\u5206\u4E3A ${groups.size} \u4E2A KeywordsForBatchJob \u5757\uFF08${labels.join("\u3001")}\uFF09`
115375
+ `${path56} \u542B\u591A\u79CD\u5339\u914D\u7C7B\u578B\uFF0C\u5DF2\u81EA\u52A8\u62C6\u5206\u4E3A ${groups.size} \u4E2A KeywordsForBatchJob \u5757\uFF08${labels.join("\u3001")}\uFF09`
115303
115376
  );
115304
115377
  const shell = cloneKeywordBlockShell(block);
115305
115378
  const splitBlocks = [];
@@ -115319,16 +115392,16 @@ function splitKeywordsForBatchJobBlockIfMixed(block, path55, warnings) {
115319
115392
  }
115320
115393
  return splitBlocks;
115321
115394
  }
115322
- function validateKeywordCore(core, path55, errors, lengthViolations) {
115395
+ function validateKeywordCore(core, path56, errors, lengthViolations) {
115323
115396
  const trimmed = core.trim();
115324
115397
  if (!trimmed) {
115325
- errors.push(`${path55} \u5173\u952E\u8BCD\u8BCD\u5E72\u4E0D\u80FD\u4E3A\u7A7A`);
115398
+ errors.push(`${path56} \u5173\u952E\u8BCD\u8BCD\u5E72\u4E0D\u80FD\u4E3A\u7A7A`);
115326
115399
  return false;
115327
115400
  }
115328
115401
  if (trimmed.length > GOOGLE_KEYWORD_MAX_CORE_LENGTH) {
115329
115402
  if (lengthViolations) {
115330
115403
  pushLengthViolation(lengthViolations, {
115331
- path: path55,
115404
+ path: path56,
115332
115405
  field: "KeywordText",
115333
115406
  kind: "keyword_core",
115334
115407
  limit: GOOGLE_KEYWORD_MAX_CORE_LENGTH,
@@ -115338,13 +115411,13 @@ function validateKeywordCore(core, path55, errors, lengthViolations) {
115338
115411
  });
115339
115412
  }
115340
115413
  errors.push(
115341
- `${path55} \u5173\u952E\u8BCD\u8BCD\u5E72\u8D85\u8FC7 ${GOOGLE_KEYWORD_MAX_CORE_LENGTH} \u5B57\u7B26\uFF08\u5F53\u524D ${trimmed.length}\uFF09\uFF1A"${trimmed}"`
115414
+ `${path56} \u5173\u952E\u8BCD\u8BCD\u5E72\u8D85\u8FC7 ${GOOGLE_KEYWORD_MAX_CORE_LENGTH} \u5B57\u7B26\uFF08\u5F53\u524D ${trimmed.length}\uFF09\uFF1A"${trimmed}"`
115342
115415
  );
115343
115416
  return false;
115344
115417
  }
115345
115418
  if (!VALID_CORE_REGEX.test(trimmed)) {
115346
115419
  errors.push(
115347
- `${path55} \u542B Google \u4E0D\u5141\u8BB8\u7684\u5B57\u7B26\uFF08\u8BCD\u5E72\u4EC5\u5141\u8BB8\u5B57\u6BCD/\u6570\u5B57/\u7A7A\u683C/\u8FDE\u5B57\u7B26/\u53E5\u70B9\uFF09\uFF1A"${trimmed.slice(0, 40)}${trimmed.length > 40 ? "\u2026" : ""}"`
115420
+ `${path56} \u542B Google \u4E0D\u5141\u8BB8\u7684\u5B57\u7B26\uFF08\u8BCD\u5E72\u4EC5\u5141\u8BB8\u5B57\u6BCD/\u6570\u5B57/\u7A7A\u683C/\u8FDE\u5B57\u7B26/\u53E5\u70B9\uFF09\uFF1A"${trimmed.slice(0, 40)}${trimmed.length > 40 ? "\u2026" : ""}"`
115348
115421
  );
115349
115422
  return false;
115350
115423
  }
@@ -115401,10 +115474,10 @@ function canonicalizeKeywordBatchBlock(block, resolvedMatchV2, normalizedTexts,
115401
115474
  }
115402
115475
  }
115403
115476
  }
115404
- function pushKeywordAutoFixWarning(warnings, path55, fieldLabel, trimmedRaw, formatted, declaredUi, beforeUi, matchType, inferredMatchType) {
115477
+ function pushKeywordAutoFixWarning(warnings, path56, fieldLabel, trimmedRaw, formatted, declaredUi, beforeUi, matchType, inferredMatchType) {
115405
115478
  if (inferredMatchType) {
115406
115479
  warnings.push(
115407
- `${path55}.${fieldLabel} \u672A\u6307\u5B9A MatchTypeV2\uFF0C\u5DF2\u6309\u8BCD\u9762\u63A8\u65AD\u4E3A ${matchTypeUiToV2(matchType)}`
115480
+ `${path56}.${fieldLabel} \u672A\u6307\u5B9A MatchTypeV2\uFF0C\u5DF2\u6309\u8BCD\u9762\u63A8\u65AD\u4E3A ${matchTypeUiToV2(matchType)}`
115408
115481
  );
115409
115482
  return false;
115410
115483
  }
@@ -115413,16 +115486,16 @@ function pushKeywordAutoFixWarning(warnings, path55, fieldLabel, trimmedRaw, for
115413
115486
  }
115414
115487
  if (declaredUi && beforeUi !== declaredUi) {
115415
115488
  warnings.push(
115416
- `${path55}.${fieldLabel} MatchTypeV2=${matchTypeUiToV2(declaredUi)} \u4E0E\u8BCD\u9762\u4E0D\u4E00\u81F4\uFF0C\u5DF2\u81EA\u52A8\u4FEE\u590D\u4E3A\uFF1A${formatted}`
115489
+ `${path56}.${fieldLabel} MatchTypeV2=${matchTypeUiToV2(declaredUi)} \u4E0E\u8BCD\u9762\u4E0D\u4E00\u81F4\uFF0C\u5DF2\u81EA\u52A8\u4FEE\u590D\u4E3A\uFF1A${formatted}`
115417
115490
  );
115418
115491
  return false;
115419
115492
  }
115420
115493
  if (formatted !== trimmedRaw) {
115421
- warnings.push(`${path55}.${fieldLabel} \u5DF2\u81EA\u52A8\u4FEE\u590D\u8BCD\u9762\uFF1A${trimmedRaw} \u2192 ${formatted}`);
115494
+ warnings.push(`${path56}.${fieldLabel} \u5DF2\u81EA\u52A8\u4FEE\u590D\u8BCD\u9762\uFF1A${trimmedRaw} \u2192 ${formatted}`);
115422
115495
  }
115423
115496
  return false;
115424
115497
  }
115425
- function normalizeKeywordTextList(texts, matchTypeRaw, path55, fieldLabel, errors, warnings, lengthViolations) {
115498
+ function normalizeKeywordTextList(texts, matchTypeRaw, path56, fieldLabel, errors, warnings, lengthViolations) {
115426
115499
  const declaredUi = matchTypeV2ToUi(matchTypeRaw);
115427
115500
  const normalized = [];
115428
115501
  const seen = /* @__PURE__ */ new Set();
@@ -115431,7 +115504,7 @@ function normalizeKeywordTextList(texts, matchTypeRaw, path55, fieldLabel, error
115431
115504
  for (let k = 0; k < texts.length; k++) {
115432
115505
  const raw = texts[k];
115433
115506
  if (typeof raw !== "string" || !raw.trim()) {
115434
- errors.push(`${path55}.${fieldLabel}[${k}] \u5FC5\u987B\u662F\u975E\u7A7A\u5B57\u7B26\u4E32`);
115507
+ errors.push(`${path56}.${fieldLabel}[${k}] \u5FC5\u987B\u662F\u975E\u7A7A\u5B57\u7B26\u4E32`);
115435
115508
  continue;
115436
115509
  }
115437
115510
  const trimmedRaw = collapseDuplicateSpacesInKeywordText(raw.trim());
@@ -115442,7 +115515,7 @@ function normalizeKeywordTextList(texts, matchTypeRaw, path55, fieldLabel, error
115442
115515
  );
115443
115516
  if (pushKeywordAutoFixWarning(
115444
115517
  warnings,
115445
- path55,
115518
+ path56,
115446
115519
  fieldLabel,
115447
115520
  trimmedRaw,
115448
115521
  formatted,
@@ -115454,11 +115527,11 @@ function normalizeKeywordTextList(texts, matchTypeRaw, path55, fieldLabel, error
115454
115527
  bareWrapped++;
115455
115528
  }
115456
115529
  const core = unwrapKeywordDisplayTextForEdit(formatted);
115457
- if (!validateKeywordCore(core, `${path55}.${fieldLabel}[${k}]`, errors, lengthViolations))
115530
+ if (!validateKeywordCore(core, `${path56}.${fieldLabel}[${k}]`, errors, lengthViolations))
115458
115531
  continue;
115459
115532
  const dedupeKey = `${matchTypeUiToV2(matchType)}:${core.toLowerCase()}`;
115460
115533
  if (seen.has(dedupeKey)) {
115461
- warnings.push(`${path55}.${fieldLabel}[${k}] \u4E0E\u540C\u7EC4\u91CD\u590D\uFF0C\u5DF2\u8DF3\u8FC7\uFF1A${formatted}`);
115534
+ warnings.push(`${path56}.${fieldLabel}[${k}] \u4E0E\u540C\u7EC4\u91CD\u590D\uFF0C\u5DF2\u8DF3\u8FC7\uFF1A${formatted}`);
115462
115535
  continue;
115463
115536
  }
115464
115537
  seen.add(dedupeKey);
@@ -115467,17 +115540,17 @@ function normalizeKeywordTextList(texts, matchTypeRaw, path55, fieldLabel, error
115467
115540
  }
115468
115541
  if (bareWrapped > 0 && declaredUi) {
115469
115542
  warnings.push(
115470
- `${path55}.${fieldLabel}\uFF1A${bareWrapped} \u4E2A\u88F8\u8BCD\u5DF2\u6309 MatchTypeV2=${matchTypeUiToV2(declaredUi)} \u81EA\u52A8\u4FEE\u590D\uFF08\u8865\u5168${declaredUi === "Exact" ? " [ ]" : "\u5F15\u53F7"}\uFF0C\u9884\u671F\u884C\u4E3A\uFF09`
115543
+ `${path56}.${fieldLabel}\uFF1A${bareWrapped} \u4E2A\u88F8\u8BCD\u5DF2\u6309 MatchTypeV2=${matchTypeUiToV2(declaredUi)} \u81EA\u52A8\u4FEE\u590D\uFF08\u8865\u5168${declaredUi === "Exact" ? " [ ]" : "\u5F15\u53F7"}\uFF0C\u9884\u671F\u884C\u4E3A\uFF09`
115471
115544
  );
115472
115545
  }
115473
115546
  return { normalized, resolvedUi };
115474
115547
  }
115475
- function normalizeKeywordsForBatchJobBlock(block, path55, errors, warnings, lengthViolations) {
115548
+ function normalizeKeywordsForBatchJobBlock(block, path56, errors, warnings, lengthViolations) {
115476
115549
  const matchTypeRaw = readBlockMatchTypeRaw(block);
115477
115550
  const declaredUi = matchTypeV2ToUi(matchTypeRaw);
115478
115551
  if (matchTypeRaw !== void 0 && declaredUi === null) {
115479
115552
  errors.push(
115480
- `${path55}.MatchTypeV2 \u65E0\u6548\uFF08${String(matchTypeRaw)}\uFF09\uFF0C\u5408\u6CD5\u503C\uFF1ABROAD | PHRASE | EXACT`
115553
+ `${path56}.MatchTypeV2 \u65E0\u6548\uFF08${String(matchTypeRaw)}\uFF09\uFF0C\u5408\u6CD5\u503C\uFF1ABROAD | PHRASE | EXACT`
115481
115554
  );
115482
115555
  return;
115483
115556
  }
@@ -115492,7 +115565,7 @@ function normalizeKeywordsForBatchJobBlock(block, path55, errors, warnings, leng
115492
115565
  const result = normalizeKeywordTextList(
115493
115566
  texts,
115494
115567
  matchTypeRaw,
115495
- path55,
115568
+ path56,
115496
115569
  "KeywordText",
115497
115570
  errors,
115498
115571
  warnings,
@@ -115501,7 +115574,7 @@ function normalizeKeywordsForBatchJobBlock(block, path55, errors, warnings, leng
115501
115574
  normalizedTexts = result.normalized;
115502
115575
  if (result.resolvedUi) resolvedUi = result.resolvedUi;
115503
115576
  if (normalizedTexts.length === 0 && texts.length > 0) {
115504
- errors.push(`${path55}.KeywordText \u7ECF\u6821\u9A8C\u540E\u65E0\u6709\u6548\u5173\u952E\u8BCD\uFF0C\u8BF7\u4FEE\u6B63\u8BCD\u9762\u6216 MatchTypeV2`);
115577
+ errors.push(`${path56}.KeywordText \u7ECF\u6821\u9A8C\u540E\u65E0\u6709\u6548\u5173\u952E\u8BCD\uFF0C\u8BF7\u4FEE\u6B63\u8BCD\u9762\u6216 MatchTypeV2`);
115505
115578
  }
115506
115579
  }
115507
115580
  if (hasItems && items3) {
@@ -115509,12 +115582,12 @@ function normalizeKeywordsForBatchJobBlock(block, path55, errors, warnings, leng
115509
115582
  for (let k = 0; k < items3.length; k++) {
115510
115583
  const item = items3[k];
115511
115584
  if (!item || typeof item !== "object") {
115512
- errors.push(`${path55}.Items[${k}] \u5FC5\u987B\u662F\u5BF9\u8C61`);
115585
+ errors.push(`${path56}.Items[${k}] \u5FC5\u987B\u662F\u5BF9\u8C61`);
115513
115586
  continue;
115514
115587
  }
115515
115588
  const raw = readItemText(item);
115516
115589
  if (raw === null || !raw.trim()) {
115517
- errors.push(`${path55}.Items[${k}].Text \u5FC5\u987B\u662F\u975E\u7A7A\u5B57\u7B26\u4E32`);
115590
+ errors.push(`${path56}.Items[${k}].Text \u5FC5\u987B\u662F\u975E\u7A7A\u5B57\u7B26\u4E32`);
115518
115591
  continue;
115519
115592
  }
115520
115593
  const trimmedRaw = collapseDuplicateSpacesInKeywordText(raw.trim());
@@ -115525,7 +115598,7 @@ function normalizeKeywordsForBatchJobBlock(block, path55, errors, warnings, leng
115525
115598
  );
115526
115599
  if (pushKeywordAutoFixWarning(
115527
115600
  warnings,
115528
- path55,
115601
+ path56,
115529
115602
  `Items[${k}].Text`,
115530
115603
  trimmedRaw,
115531
115604
  formatted,
@@ -115534,14 +115607,14 @@ function normalizeKeywordsForBatchJobBlock(block, path55, errors, warnings, leng
115534
115607
  matchType,
115535
115608
  inferredMatchType
115536
115609
  )) {
115537
- warnings.push(`${path55}.Items[${k}].Text \u5DF2\u6309\u5757\u7EA7\u5339\u914D\u7C7B\u578B\u81EA\u52A8\u4FEE\u590D\u4E3A\uFF1A${formatted}`);
115610
+ warnings.push(`${path56}.Items[${k}].Text \u5DF2\u6309\u5757\u7EA7\u5339\u914D\u7C7B\u578B\u81EA\u52A8\u4FEE\u590D\u4E3A\uFF1A${formatted}`);
115538
115611
  }
115539
115612
  const core = unwrapKeywordDisplayTextForEdit(formatted);
115540
- if (!validateKeywordCore(core, `${path55}.Items[${k}].Text`, errors, lengthViolations))
115613
+ if (!validateKeywordCore(core, `${path56}.Items[${k}].Text`, errors, lengthViolations))
115541
115614
  continue;
115542
115615
  const dedupeKey = `${matchTypeUiToV2(matchType)}:${core.toLowerCase()}`;
115543
115616
  if (seen.has(dedupeKey)) {
115544
- warnings.push(`${path55}.Items[${k}].Text \u4E0E\u540C\u7EC4\u91CD\u590D\uFF0C\u5DF2\u8DF3\u8FC7\uFF1A${formatted}`);
115617
+ warnings.push(`${path56}.Items[${k}].Text \u4E0E\u540C\u7EC4\u91CD\u590D\uFF0C\u5DF2\u8DF3\u8FC7\uFF1A${formatted}`);
115545
115618
  continue;
115546
115619
  }
115547
115620
  seen.add(dedupeKey);
@@ -115687,16 +115760,16 @@ function warnIfNegativeKeywordsInPositiveKeywords(campaign, warnings) {
115687
115760
  const raw = texts[ki];
115688
115761
  if (typeof raw !== "string" || !raw.trim()) continue;
115689
115762
  const core = normalizeKeywordCoreForNegativeCheck(raw);
115690
- const path55 = `campaign.AdGroupsForBatchJob[${gi}].KeywordsForBatchJob[${bi}].KeywordText[${ki}]`;
115763
+ const path56 = `campaign.AdGroupsForBatchJob[${gi}].KeywordsForBatchJob[${bi}].KeywordText[${ki}]`;
115691
115764
  if (plannedNegCores.has(core)) {
115692
115765
  warnings.push(
115693
- `${path55} \u8BCD\u9762\u300C${raw}\u300D\u5DF2\u51FA\u73B0\u5728 campaign.NegativeKeywordsForBatchJob\uFF0C\u4E0D\u5E94\u91CD\u590D\u5199\u5165\u6B63\u5411 KeywordsForBatchJob\uFF1B\u5426\u8BCD\u53EA\u653E NegativeKeywordsForBatchJob`
115766
+ `${path56} \u8BCD\u9762\u300C${raw}\u300D\u5DF2\u51FA\u73B0\u5728 campaign.NegativeKeywordsForBatchJob\uFF0C\u4E0D\u5E94\u91CD\u590D\u5199\u5165\u6B63\u5411 KeywordsForBatchJob\uFF1B\u5426\u8BCD\u53EA\u653E NegativeKeywordsForBatchJob`
115694
115767
  );
115695
115768
  continue;
115696
115769
  }
115697
115770
  if (looksLikeStandaloneNegativeKeyword(core)) {
115698
115771
  warnings.push(
115699
- `${path55} \u8BCD\u9762\u300C${raw}\u300D\u7591\u4F3C\u5426\u5B9A\u5173\u952E\u8BCD\uFF08\u5E38\u89C1\u5426\u8BCD\u8BCD\u6839\uFF09\uFF0C\u5E94\u5199\u5165 campaign.NegativeKeywordsForBatchJob\uFF08MatchTypeV2: BROAD\uFF09\uFF0C\u52FF\u653E\u5165 KeywordsForBatchJob`
115772
+ `${path56} \u8BCD\u9762\u300C${raw}\u300D\u7591\u4F3C\u5426\u5B9A\u5173\u952E\u8BCD\uFF08\u5E38\u89C1\u5426\u8BCD\u8BCD\u6839\uFF09\uFF0C\u5E94\u5199\u5165 campaign.NegativeKeywordsForBatchJob\uFF08MatchTypeV2: BROAD\uFF09\uFF0C\u52FF\u653E\u5165 KeywordsForBatchJob`
115700
115773
  );
115701
115774
  }
115702
115775
  }
@@ -116632,8 +116705,41 @@ function parseCampaignTimeMs(v) {
116632
116705
  const t = new Date(v).getTime();
116633
116706
  return Number.isFinite(t) ? t : null;
116634
116707
  }
116708
+ async function emitReusedDigestCampaignTypes(opts, hit) {
116709
+ process.stderr.write(
116710
+ `\u5DF2\u6709 accounts-digest \u7CFB\u5217\u7C7B\u578B\uFF08${hit.sourceFile}\uFF09\uFF0C\u672A\u8BF7\u6C42\u5E7F\u544A\u7CFB\u5217\u5217\u8868\u3002channelTypeV2 \u4E0E campaignTypes \u76F8\u540C\u3002\u8981\u7CFB\u5217\u540D\u79F0\u3001\u72B6\u6001\u3001\u6D88\u8017\u8BF7\u52A0 --live\u3002
116711
+ `
116712
+ );
116713
+ const payload = {
116714
+ ok: true,
116715
+ source: "accounts-digest",
116716
+ mediaCustomerId: hit.mediaCustomerId,
116717
+ campaignTypes: hit.campaignTypes,
116718
+ channelTypeV2: hit.channelTypeV2,
116719
+ hasDisplay: hit.hasDisplay,
116720
+ hasPmax: hit.hasPmax,
116721
+ note: "\u7CFB\u5217\u7C7B\u578B\u6765\u81EA\u5DE5\u4F5C\u76EE\u5F55\u91CC\u5DF2\u6709\u7684 accounts-digest\uFF0C\u4E0D\u662F\u5E7F\u544A\u7CFB\u5217\u5217\u8868\u3002"
116722
+ };
116723
+ if (await emitCliJsonOrSnapshot(opts, {
116724
+ section: `ad-campaigns-${opts.account}`,
116725
+ commandLabel: "ad campaigns",
116726
+ commandHint: opts.account,
116727
+ payload,
116728
+ idSuffix: opts.account
116729
+ })) {
116730
+ return;
116731
+ }
116732
+ console.log(JSON.stringify(payload));
116733
+ }
116635
116734
  async function runAdCampaigns(opts) {
116636
116735
  opts = withResolvedAdAccount(opts);
116736
+ if (!opts.live) {
116737
+ const hit = findDigestCampaignTypes(process.cwd(), opts.account);
116738
+ if (hit) {
116739
+ await emitReusedDigestCampaignTypes(opts, hit);
116740
+ return;
116741
+ }
116742
+ }
116637
116743
  const config = loadConfig(opts.token);
116638
116744
  const googleApiUrl = requireGoogleApi(config);
116639
116745
  const url = campaignListUrl(googleApiUrl, opts.account, opts.startDate, opts.endDate, opts.fresh);
@@ -119414,8 +119520,8 @@ init_cli_table();
119414
119520
  init_auth();
119415
119521
  init_cli_json_snapshot();
119416
119522
  init_shared();
119417
- import fs9 from "fs";
119418
- import path14 from "path";
119523
+ import fs10 from "fs";
119524
+ import path15 from "path";
119419
119525
 
119420
119526
  // src/commands/ad/campaign-extension-remediate.ts
119421
119527
  function shellArg(value) {
@@ -119559,7 +119665,7 @@ function buildBatchOperationSlots(cfg) {
119559
119665
  const campaign = asRecord3(cfg.campaign) ?? {};
119560
119666
  const names = Array.isArray(cfg.locations) ? cfg.locations : [];
119561
119667
  const slots = [];
119562
- const push = (kind, path55, label2) => slots.push({ index: slots.length, kind, path: path55, label: label2 });
119668
+ const push = (kind, path56, label2) => slots.push({ index: slots.length, kind, path: path56, label: label2 });
119563
119669
  push("campaign_budget", "campaign.Budget", "\u7CFB\u5217\u9884\u7B97");
119564
119670
  push("campaign", "campaign.Name", `\u7CFB\u5217\u300C${pickText(campaign, "Name", "name") || "\u2014"}\u300D`);
119565
119671
  pickArray(campaign, "targetedLocations", "TargetedLocations").forEach((loc, i) => {
@@ -120042,13 +120148,13 @@ function compareCampaignCreateToLive(cfg, campaignId, live, meta) {
120042
120148
  for (let ki = 0; ki < texts.length; ki++) {
120043
120149
  const t = texts[ki];
120044
120150
  if (typeof t !== "string" || !t.trim()) continue;
120045
- const path55 = `${groupPath}.KeywordsForBatchJob[${bi}].KeywordText[${ki}]`;
120151
+ const path56 = `${groupPath}.KeywordsForBatchJob[${bi}].KeywordText[${ki}]`;
120046
120152
  const key = keywordKey(t, matchTypeV2);
120047
120153
  const plannedContent = `\u8BCD\u9762: ${t} | \u5339\u914D: ${matchTypeV2}`;
120048
120154
  if (groupAbsent) {
120049
120155
  pushItem(items3, {
120050
120156
  layer: "keyword",
120051
- path: path55,
120157
+ path: path56,
120052
120158
  adGroupName: groupName,
120053
120159
  plannedContent,
120054
120160
  status: "skipped",
@@ -120060,7 +120166,7 @@ function compareCampaignCreateToLive(cfg, campaignId, live, meta) {
120060
120166
  if (!liveKwKeys.has(key)) {
120061
120167
  pushItem(items3, {
120062
120168
  layer: "keyword",
120063
- path: path55,
120169
+ path: path56,
120064
120170
  adGroupName: groupName,
120065
120171
  plannedContent,
120066
120172
  status: "missing",
@@ -120070,7 +120176,7 @@ function compareCampaignCreateToLive(cfg, campaignId, live, meta) {
120070
120176
  } else {
120071
120177
  pushItem(items3, {
120072
120178
  layer: "keyword",
120073
- path: path55,
120179
+ path: path56,
120074
120180
  adGroupName: groupName,
120075
120181
  plannedContent,
120076
120182
  status: "ok",
@@ -120090,7 +120196,7 @@ function compareCampaignCreateToLive(cfg, campaignId, live, meta) {
120090
120196
  for (let ai = 0; ai < ads.length; ai++) {
120091
120197
  const ad = asRecord4(ads[ai]);
120092
120198
  if (!ad) continue;
120093
- const path55 = `${groupPath}.AdsForBatchJob[${ai}]`;
120199
+ const path56 = `${groupPath}.AdsForBatchJob[${ai}]`;
120094
120200
  const primary = pickString2(ad["headlinePart1"], ad["AdTitle"]);
120095
120201
  const finalUrl = pickString2(ad["Finalurl"], ad["DestinationUrl"], ad["finalUrl"]);
120096
120202
  const rsaAssets = listPlannedRsaAssets(ad, groupPath, ai, groupName);
@@ -120099,7 +120205,7 @@ function compareCampaignCreateToLive(cfg, campaignId, live, meta) {
120099
120205
  if (groupAbsent) {
120100
120206
  pushItem(items3, {
120101
120207
  layer: "ad",
120102
- path: path55,
120208
+ path: path56,
120103
120209
  adGroupName: groupName,
120104
120210
  adLabel,
120105
120211
  plannedContent: primary ? `RSA \u9996\u6807\u9898: ${primary}${finalUrl ? ` | \u843D\u5730\u9875: ${finalUrl}` : ""}` : `AdsForBatchJob[${ai}]\uFF08\u7F3A\u5C11 headlinePart1\uFF09`,
@@ -120128,7 +120234,7 @@ function compareCampaignCreateToLive(cfg, campaignId, live, meta) {
120128
120234
  if (!primary) {
120129
120235
  pushItem(items3, {
120130
120236
  layer: "ad",
120131
- path: path55,
120237
+ path: path56,
120132
120238
  adGroupName: groupName,
120133
120239
  adLabel,
120134
120240
  plannedContent: `AdsForBatchJob[${ai}]\uFF08\u7F3A\u5C11 headlinePart1\uFF0C\u65E0\u6CD5\u6BD4\u5BF9\u6574\u6761 RSA\uFF09`,
@@ -120156,7 +120262,7 @@ function compareCampaignCreateToLive(cfg, campaignId, live, meta) {
120156
120262
  const rsaFound = matchedLive != null && liveAdHasHeadline(matchedLive, primary);
120157
120263
  pushItem(items3, {
120158
120264
  layer: "ad",
120159
- path: path55,
120265
+ path: path56,
120160
120266
  adGroupName: groupName,
120161
120267
  adLabel,
120162
120268
  plannedContent: `RSA \u9996\u6807\u9898: ${primary}${finalUrl ? ` | \u843D\u5730\u9875: ${finalUrl}` : ""}`,
@@ -120224,11 +120330,11 @@ function compareCampaignCreateToLive(cfg, campaignId, live, meta) {
120224
120330
  for (let ei = 0; ei < extBlocks.length; ei++) {
120225
120331
  const ext = asRecord4(extBlocks[ei]);
120226
120332
  const plannedContent = formatExtensionPlanned(ext);
120227
- const path55 = `campaign.ExtensionsForBatchJob[${ei}]`;
120333
+ const path56 = `campaign.ExtensionsForBatchJob[${ei}]`;
120228
120334
  if (campaignMissing) {
120229
120335
  pushItem(items3, {
120230
120336
  layer: "extension",
120231
- path: path55,
120337
+ path: path56,
120232
120338
  plannedContent,
120233
120339
  status: "skipped",
120234
120340
  liveNote: "\u7CFB\u5217\u672A\u627E\u5230\uFF0C\u8DF3\u8FC7\u9644\u52A0\u4FE1\u606F\u6BD4\u5BF9",
@@ -120240,7 +120346,7 @@ function compareCampaignCreateToLive(cfg, campaignId, live, meta) {
120240
120346
  const remediateCommand = !found && ext && accountId && campaignId ? buildExtensionRemediateCommandLine({ accountId, campaignId, ext }) ?? void 0 : void 0;
120241
120347
  pushItem(items3, {
120242
120348
  layer: "extension",
120243
- path: path55,
120349
+ path: path56,
120244
120350
  plannedContent,
120245
120351
  status: found ? "ok" : "missing",
120246
120352
  liveNote: `\u7CFB\u5217\u7EA7\u9644\u52A0\u4FE1\u606F\u8D26\u6237\u5185 ${liveExt}/${plannedExt} \u6761`,
@@ -120267,11 +120373,11 @@ function compareCampaignCreateToLive(cfg, campaignId, live, meta) {
120267
120373
  if (!id) continue;
120268
120374
  const displayName = locationNames[li] || pickString2(loc["locationName"], loc["name"], loc["countryOrRegion"]) || "";
120269
120375
  const plannedContent = `locationId: ${id}${displayName ? ` | \u540D\u79F0: ${displayName}` : ""}`;
120270
- const path55 = `campaign.targetedLocations[${li}]`;
120376
+ const path56 = `campaign.targetedLocations[${li}]`;
120271
120377
  if (campaignMissing) {
120272
120378
  pushItem(items3, {
120273
120379
  layer: "location",
120274
- path: path55,
120380
+ path: path56,
120275
120381
  plannedContent,
120276
120382
  status: "skipped",
120277
120383
  liveNote: "\u7CFB\u5217\u672A\u627E\u5230\uFF0C\u8DF3\u8FC7\u5730\u57DF\u6BD4\u5BF9",
@@ -120283,7 +120389,7 @@ function compareCampaignCreateToLive(cfg, campaignId, live, meta) {
120283
120389
  const remediateCommand = !found && accountId && campaignId ? buildGeoRemediateCommandLine({ accountId, campaignId, locationId: id }) : void 0;
120284
120390
  pushItem(items3, {
120285
120391
  layer: "location",
120286
- path: path55,
120392
+ path: path56,
120287
120393
  plannedContent,
120288
120394
  status: found ? "ok" : "missing",
120289
120395
  liveNote: `\u7CFB\u5217\u5DF2\u5B9A\u5411 ${liveLocIds.size}/${plannedLocCount} \u4E2A\u5730\u57DF`,
@@ -120303,12 +120409,12 @@ function compareCampaignCreateToLive(cfg, campaignId, live, meta) {
120303
120409
  const loc = asRecord4(plannedExcl[li]);
120304
120410
  const id = loc ? pickString2(loc["id"], loc["Id"]) : "";
120305
120411
  if (!id) continue;
120306
- const path55 = `campaign.excludedLocations[${li}]`;
120412
+ const path56 = `campaign.excludedLocations[${li}]`;
120307
120413
  const plannedContent = `\u6392\u9664 locationId: ${id}`;
120308
120414
  if (campaignMissing) {
120309
120415
  pushItem(items3, {
120310
120416
  layer: "excluded_location",
120311
- path: path55,
120417
+ path: path56,
120312
120418
  plannedContent,
120313
120419
  status: "skipped",
120314
120420
  liveNote: "\u7CFB\u5217\u672A\u627E\u5230\uFF0C\u8DF3\u8FC7\u6392\u9664\u5730\u57DF\u6BD4\u5BF9",
@@ -120319,7 +120425,7 @@ function compareCampaignCreateToLive(cfg, campaignId, live, meta) {
120319
120425
  const found = liveExclIds.has(id);
120320
120426
  pushItem(items3, {
120321
120427
  layer: "excluded_location",
120322
- path: path55,
120428
+ path: path56,
120323
120429
  plannedContent,
120324
120430
  status: found ? "ok" : "missing",
120325
120431
  liveNote: `\u7CFB\u5217\u5DF2\u6392\u9664 ${liveExclIds.size}/${plannedExclCount} \u4E2A\u5730\u57DF`,
@@ -120835,23 +120941,23 @@ function buildCampaignCreateStatusMarkdown(result) {
120835
120941
  return lines.join("\n");
120836
120942
  }
120837
120943
  function resolveBatchDiffMdOutPath(opts) {
120838
- if (opts.mdOut?.trim()) return path14.resolve(opts.mdOut.trim());
120944
+ if (opts.mdOut?.trim()) return path15.resolve(opts.mdOut.trim());
120839
120945
  const fileName = `campaign-create-status-${opts.campaignId || "unknown"}.md`;
120840
120946
  if (opts.jsonOut?.trim()) {
120841
120947
  const jsonOut = opts.jsonOut.trim();
120842
- const abs = path14.resolve(jsonOut);
120948
+ const abs = path15.resolve(jsonOut);
120843
120949
  const looksLikeFile = /\.json$/i.test(jsonOut);
120844
120950
  if (looksLikeFile) {
120845
- return path14.join(path14.dirname(abs), fileName);
120951
+ return path15.join(path15.dirname(abs), fileName);
120846
120952
  }
120847
- return path14.join(abs, fileName);
120953
+ return path15.join(abs, fileName);
120848
120954
  }
120849
- return path14.resolve(fileName);
120955
+ return path15.resolve(fileName);
120850
120956
  }
120851
120957
  function writeCampaignCreateStatusMarkdown(result, mdPath) {
120852
120958
  const reportMarkdown = buildCampaignCreateStatusMarkdown(result);
120853
- fs9.mkdirSync(path14.dirname(mdPath), { recursive: true });
120854
- fs9.writeFileSync(mdPath, reportMarkdown, "utf8");
120959
+ fs10.mkdirSync(path15.dirname(mdPath), { recursive: true });
120960
+ fs10.writeFileSync(mdPath, reportMarkdown, "utf8");
120855
120961
  return { reportMarkdown, reportMarkdownFile: mdPath };
120856
120962
  }
120857
120963
  var USER_DELIVERY_MARKDOWN_BEGIN = "\u2500\u2500 BEGIN_USER_DELIVERY_MARKDOWN \uFF08\u5FC5\u987B\u539F\u6837\u7C98\u8D34\u5230\u5BF9\u7528\u6237\u7684\u4E0B\u4E00\u6761\u6D88\u606F\uFF1B\u7981\u6B62\u6458\u8981\u4EE3\u66FF\uFF1B\u4EA4\u4ED8\u4F18\u5148\u4E8E\u8865\u5EFA\uFF09\u2500\u2500";
@@ -121969,8 +122075,8 @@ function pmaxChannelTypesUrl(googleApiUrl) {
121969
122075
  }
121970
122076
  function pmaxCampaignUrl(googleApiUrl, accountId, campaignId) {
121971
122077
  const base = googleApiUrl.replace(/\/$/, "");
121972
- const path55 = `${base}/accounts/${accountId}/campaign/pmax`;
121973
- return campaignId ? `${path55}/${campaignId}` : path55;
122078
+ const path56 = `${base}/accounts/${accountId}/campaign/pmax`;
122079
+ return campaignId ? `${path56}/${campaignId}` : path56;
121974
122080
  }
121975
122081
  function pmaxBrandAssetsUrl(googleApiUrl, accountId, campaignId) {
121976
122082
  return `${pmaxCampaignUrl(googleApiUrl, accountId, campaignId)}/brand-assets`;
@@ -121980,8 +122086,8 @@ function pmaxBrandGuidelinesEnableUrl(googleApiUrl, accountId, campaignId) {
121980
122086
  }
121981
122087
  function pmaxAssetGroupUrl(googleApiUrl, accountId, assetGroupId, suffix) {
121982
122088
  const base = googleApiUrl.replace(/\/$/, "");
121983
- const path55 = `${base}/accounts/${accountId}/campaign/pmax/asset-group/${assetGroupId}`;
121984
- return suffix ? `${path55}/${suffix.replace(/^\//, "")}` : path55;
122089
+ const path56 = `${base}/accounts/${accountId}/campaign/pmax/asset-group/${assetGroupId}`;
122090
+ return suffix ? `${path56}/${suffix.replace(/^\//, "")}` : path56;
121985
122091
  }
121986
122092
  function pmaxCampaignAssetGroupUrl(googleApiUrl, accountId, campaignId) {
121987
122093
  const base = googleApiUrl.replace(/\/$/, "");
@@ -124732,8 +124838,8 @@ async function runAdPmaxImageConvert(opts) {
124732
124838
  }
124733
124839
 
124734
124840
  // src/commands/ad/pmax-brand-mgmt.ts
124735
- function loadBrandConfig(path55, accountId, campaignId) {
124736
- const cfg = loadPmaxJsonFile(path55);
124841
+ function loadBrandConfig(path56, accountId, campaignId) {
124842
+ const cfg = loadPmaxJsonFile(path56);
124737
124843
  return {
124738
124844
  ...cfg,
124739
124845
  account: String(cfg["account"] ?? accountId).trim(),
@@ -124976,6 +125082,10 @@ function register21(program2) {
124976
125082
  "--fresh",
124977
125083
  "\u8DF3\u8FC7\u7F51\u5173 60 \u79D2\u7F13\u5B58\uFF0C\u5F3A\u5236\u62C9\u53D6\u6700\u65B0\u6570\u636E\uFF08\u7F51\u5173 newest=true\uFF09\uFF1B\u521A\u521B\u5EFA/\u7F16\u8F91\u5B8C\u60F3\u7ACB\u523B\u770B\u5230\u7ED3\u679C\u65F6\u7528",
124978
125084
  false
125085
+ ).option(
125086
+ "--live",
125087
+ "\u5FFD\u7565\u5DE5\u4F5C\u76EE\u5F55\u91CC\u5DF2\u6709\u7684 accounts-digest \u7CFB\u5217\u7C7B\u578B\uFF0C\u5F3A\u5236\u8BF7\u6C42\u7CFB\u5217\u5217\u8868\uFF08\u8981\u7CFB\u5217\u540D\u79F0\u3001\u72B6\u6001\u3001\u6D88\u8017\u65F6\u7528\uFF1B\u5DE1\u68C0\u4E0D\u8981\u52A0\uFF09",
125088
+ false
124979
125089
  ).option("-t, --token <token>", "Auth Token").option(
124980
125090
  "--json-out <path>",
124981
125091
  "\u843D\u76D8\uFF08\u76EE\u5F55\u6216 *.json \u6587\u4EF6\u8DEF\u5F84\uFF09\u5E76\u66F4\u65B0 cli-manifest[-<\u67E5\u8BE2id>].json\uFF1B\u76EE\u5F55\u6A21\u5F0F\u6587\u4EF6\u540D\u4E3A `<section>[-<\u67E5\u8BE2id>].json`\uFF1Bstdout \u4E00\u884C\u6458\u8981 JSON\uFF0C\u542B outlineFile\uFF08TS \u5F0F\u7C7B\u578B\u5728 `*.outline.txt`\uFF09",
@@ -124988,6 +125098,7 @@ function register21(program2) {
124988
125098
  startDate: opts.start,
124989
125099
  endDate: opts.end,
124990
125100
  fresh: opts.fresh,
125101
+ live: opts.live,
124991
125102
  jsonOut: opts.jsonOut,
124992
125103
  verbose: opts.verbose
124993
125104
  });
@@ -126393,9 +126504,9 @@ function metaAdContext(token) {
126393
126504
  const config = loadConfig(token);
126394
126505
  return { config, facebookApiUrl: requireFacebookApi(config) };
126395
126506
  }
126396
- function adsUrl(facebookApiUrl, account, path55) {
126507
+ function adsUrl(facebookApiUrl, account, path56) {
126397
126508
  const base = metaAdsBaseUrl(facebookApiUrl, account);
126398
- const p = path55.startsWith("/") ? path55 : `/${path55}`;
126509
+ const p = path56.startsWith("/") ? path56 : `/${path56}`;
126399
126510
  return `${base}${p}`;
126400
126511
  }
126401
126512
  async function metaAdGet(url, config, verbose, action) {
@@ -127602,9 +127713,9 @@ async function runMetaAdForms(opts) {
127602
127713
  if (opts.after) params.set("after", opts.after);
127603
127714
  if (opts.before) params.set("before", opts.before);
127604
127715
  const qs = params.toString();
127605
- const path55 = `/pages/${encodeURIComponent(opts.pageId)}/forms${qs ? `?${qs}` : ""}`;
127716
+ const path56 = `/pages/${encodeURIComponent(opts.pageId)}/forms${qs ? `?${qs}` : ""}`;
127606
127717
  const raw = await metaAdGet(
127607
- adsUrl(facebookApiUrl, digits, path55),
127718
+ adsUrl(facebookApiUrl, digits, path56),
127608
127719
  config,
127609
127720
  opts.verbose,
127610
127721
  "\u5217\u8868\u5355\u5931\u8D25"
@@ -127699,8 +127810,8 @@ async function runMetaAdFormCreate(opts) {
127699
127810
 
127700
127811
  // src/commands/meta-ad/plan-render.ts
127701
127812
  init_xlsx_style_engine();
127702
- import fs11 from "fs";
127703
- import path16 from "path";
127813
+ import fs12 from "fs";
127814
+ import path17 from "path";
127704
127815
  var PENDING = "[\u5F85\u8865]";
127705
127816
  var SHEET_NAMES = ["\u65B9\u6848\u603B\u89C8", "1-\u5BA2\u6237\u753B\u50CF", "2-\u8D26\u6237\u7ED3\u6784", "3-\u8868\u5355\u8BE2\u76D8C1"];
127706
127817
  var OPS = {
@@ -128081,17 +128192,17 @@ function writeMetaLeadPlanExcel(outPath, cfg) {
128081
128192
  return writeOpsExcel(outPath, cfg);
128082
128193
  }
128083
128194
  function writeMetaLeadPlanMarkdown(outPath, cfg) {
128084
- const abs = path16.resolve(outPath);
128085
- fs11.mkdirSync(path16.dirname(abs), { recursive: true });
128086
- fs11.writeFileSync(abs, renderMarkdown(cfg), "utf8");
128195
+ const abs = path17.resolve(outPath);
128196
+ fs12.mkdirSync(path17.dirname(abs), { recursive: true });
128197
+ fs12.writeFileSync(abs, renderMarkdown(cfg), "utf8");
128087
128198
  return abs;
128088
128199
  }
128089
128200
  async function runMetaAdPlanRender(opts) {
128090
128201
  const cfg = loadMetaLeadCreateConfig(opts.configFile);
128091
- const configDir = path16.dirname(path16.resolve(opts.configFile));
128092
- const xlsxOut = path16.resolve(opts.out || path16.join(configDir, "meta-lead-plan.xlsx"));
128093
- const mdOut = path16.resolve(
128094
- opts.mdOut || path16.join(path16.dirname(xlsxOut), `${path16.parse(xlsxOut).name}.md`)
128202
+ const configDir = path17.dirname(path17.resolve(opts.configFile));
128203
+ const xlsxOut = path17.resolve(opts.out || path17.join(configDir, "meta-lead-plan.xlsx"));
128204
+ const mdOut = path17.resolve(
128205
+ opts.mdOut || path17.join(path17.dirname(xlsxOut), `${path17.parse(xlsxOut).name}.md`)
128095
128206
  );
128096
128207
  const xlsxPath = writeMetaLeadPlanExcel(xlsxOut, cfg);
128097
128208
  const mdPath = writeMetaLeadPlanMarkdown(mdOut, cfg);
@@ -129429,6 +129540,13 @@ async function runKeywordSuggest(opts) {
129429
129540
  return !excludes.some((exc) => kw.includes(exc));
129430
129541
  });
129431
129542
  }
129543
+ const beforeVolumeFilter = items3.length;
129544
+ items3 = filterByMinMonthlySearch(items3, opts.minMonthlySearch);
129545
+ if (opts.minMonthlySearch != null && opts.minMonthlySearch > 0 && items3.length !== beforeVolumeFilter) {
129546
+ console.error(
129547
+ ` [keyword] \u6708\u641C\u7D22\u91CF >= ${opts.minMonthlySearch}\uFF1A\u4FDD\u7559 ${items3.length} \u6761\uFF0C\u53BB\u6389 ${beforeVolumeFilter - items3.length} \u6761\uFF08\u7F3A\u641C\u7D22\u91CF\u89C6\u4E3A 0\uFF09`
129548
+ );
129549
+ }
129432
129550
  if (accountId) {
129433
129551
  try {
129434
129552
  const accountNormSet = await fetchAccountPositiveKeywordNormSet(
@@ -129566,6 +129684,23 @@ function microsItemToBidAmounts(item) {
129566
129684
  )
129567
129685
  };
129568
129686
  }
129687
+ function filterByMinMonthlySearch(items3, minMonthlySearch) {
129688
+ if (minMonthlySearch == null || !Number.isFinite(minMonthlySearch) || minMonthlySearch <= 0) {
129689
+ return items3;
129690
+ }
129691
+ return items3.filter((item) => {
129692
+ const volume = Number(item.montlySearch);
129693
+ return Number.isFinite(volume) && volume >= minMonthlySearch;
129694
+ });
129695
+ }
129696
+ function parseMinMonthlySearch(raw) {
129697
+ const value = Number(raw);
129698
+ if (!Number.isFinite(value) || value < 0) {
129699
+ console.error("\n\u274C --min-monthly-search \u5FC5\u987B\u662F\u5927\u4E8E\u7B49\u4E8E 0 \u7684\u6570\u5B57\u3002\n");
129700
+ process.exit(1);
129701
+ }
129702
+ return value;
129703
+ }
129569
129704
  function attachBaseCliOptions(cmd) {
129570
129705
  attachJsonOutOption(cmd);
129571
129706
  cmd.option("--verbose", "\u8F93\u51FA\u8BE6\u7EC6\u9519\u8BEF\u5806\u6808").option("--token <jwt>", "\u4E34\u65F6\u4F7F\u7528\u7684 JWT Token\uFF08\u8986\u76D6 ~/.siluzan/config.json\uFF09");
@@ -129584,7 +129719,10 @@ function register23(program2) {
129584
129719
  ).option(
129585
129720
  "--geo <ids>",
129586
129721
  "geoTargetConstant ID\uFF08\u5148 keyword geo-list\uFF1B\u4F8B 2840=\u7F8E\u56FD\uFF09\u3002\u591A\u4E2A ID \u4E3A\u6C47\u603B\u6307\u6807\uFF1B\u5206\u5E02\u573A\u987B\u591A\u6B21\u8C03\u7528\u3001\u6BCF\u6B21\u53EA\u4F20\u4E00\u4E2A"
129587
- ).option("--url <url>", "\u516C\u53F8/\u4EA7\u54C1\u7F51\u5740\uFF08\u586B\u5199\u540E\u89E6\u53D1\u7F51\u5740\u62D3\u8BCD\uFF1B\u4E0E --google-only \u4E92\u65A5\uFF09").option("--google-only", "\u4EC5 Google Keyword Planner\uFF0C\u4E0D\u53E0\u52A0\u7F51\u5740\u62D3\u8BCD").option("--include <words>", "\u7ED3\u679C\u5FC5\u987B\u5305\u542B\u7684\u8BCD\uFF0C\u9017\u53F7\u6216\u7A7A\u683C\u5206\u9694").option("--exclude <words>", "\u7ED3\u679C\u4E0D\u5305\u542B\u7684\u8BCD\uFF0C\u9017\u53F7\u6216\u7A7A\u683C\u5206\u9694").action(async (opts) => {
129722
+ ).option("--url <url>", "\u516C\u53F8/\u4EA7\u54C1\u7F51\u5740\uFF08\u586B\u5199\u540E\u89E6\u53D1\u7F51\u5740\u62D3\u8BCD\uFF1B\u4E0E --google-only \u4E92\u65A5\uFF09").option("--google-only", "\u4EC5 Google Keyword Planner\uFF0C\u4E0D\u53E0\u52A0\u7F51\u5740\u62D3\u8BCD").option("--include <words>", "\u7ED3\u679C\u5FC5\u987B\u5305\u542B\u7684\u8BCD\uFF0C\u9017\u53F7\u6216\u7A7A\u683C\u5206\u9694").option("--exclude <words>", "\u7ED3\u679C\u4E0D\u5305\u542B\u7684\u8BCD\uFF0C\u9017\u53F7\u6216\u7A7A\u683C\u5206\u9694").option(
129723
+ "--min-monthly-search <n>",
129724
+ "\u4E22\u6389\u6708\u641C\u7D22\u91CF\u4F4E\u4E8E\u8BE5\u503C\u7684\u8BCD\uFF1B\u7F3A\u641C\u7D22\u91CF\u89C6\u4E3A 0\u3002\u7528\u6237\u8981\u6709\u641C\u7D22\u70ED\u5EA6\u65F6\u7528 1"
129725
+ ).action(async (opts) => {
129588
129726
  await runKeywordSuggest({
129589
129727
  token: opts.token,
129590
129728
  account: opts.account,
@@ -129594,6 +129732,7 @@ function register23(program2) {
129594
129732
  include: opts.include,
129595
129733
  exclude: opts.exclude,
129596
129734
  googleOnly: opts.googleOnly,
129735
+ minMonthlySearch: opts.minMonthlySearch == null ? void 0 : parseMinMonthlySearch(opts.minMonthlySearch),
129597
129736
  jsonOut: opts.jsonOut,
129598
129737
  verbose: opts.verbose
129599
129738
  });
@@ -132151,8 +132290,8 @@ async function runOpenAccountYandex(opts) {
132151
132290
 
132152
132291
  // src/commands/open-account/bing.ts
132153
132292
  init_auth();
132154
- import * as fs12 from "fs";
132155
- import * as path17 from "path";
132293
+ import * as fs13 from "fs";
132294
+ import * as path18 from "path";
132156
132295
  init_cli_json_snapshot();
132157
132296
 
132158
132297
  // src/commands/open-account/bing-industry-dict.ts
@@ -132280,8 +132419,8 @@ async function fetchBingIndustryOptions(config, verbose) {
132280
132419
 
132281
132420
  // src/commands/open-account/bing.ts
132282
132421
  async function uploadAttachment(filePath, apiBaseUrl, config, verbose) {
132283
- const fileName = path17.basename(filePath);
132284
- const fileBuffer = fs12.readFileSync(filePath);
132422
+ const fileName = path18.basename(filePath);
132423
+ const fileBuffer = fs13.readFileSync(filePath);
132285
132424
  const mimeType = guessContentType2(fileName);
132286
132425
  const form = new FormData();
132287
132426
  form.append("file", new Blob([fileBuffer], { type: mimeType }), fileName);
@@ -132309,7 +132448,7 @@ async function uploadAttachment(filePath, apiBaseUrl, config, verbose) {
132309
132448
  return { id: data.id, fileName, storageProvider: data.storageProvider ?? "StorageAccount" };
132310
132449
  }
132311
132450
  function guessContentType2(fileName) {
132312
- const ext = path17.extname(fileName).toLowerCase();
132451
+ const ext = path18.extname(fileName).toLowerCase();
132313
132452
  const map = {
132314
132453
  ".jpg": "image/jpeg",
132315
132454
  ".jpeg": "image/jpeg",
@@ -132322,7 +132461,7 @@ function guessContentType2(fileName) {
132322
132461
  }
132323
132462
  async function runOpenAccountBing(opts) {
132324
132463
  const config = loadConfig(opts.token);
132325
- if (!fs12.existsSync(opts.licenseFile)) {
132464
+ if (!fs13.existsSync(opts.licenseFile)) {
132326
132465
  console.error(`
132327
132466
  \u274C \u8425\u4E1A\u6267\u7167\u6587\u4EF6\u4E0D\u5B58\u5728\uFF1A${opts.licenseFile}
132328
132467
  `);
@@ -132824,13 +132963,13 @@ Google \u5F00\u6237\u884C\u4E1A\uFF08\u7B2C 1 \u9875\uFF0C\u672C\u9875 ${rows.le
132824
132963
 
132825
132964
  // src/commands/open-account/tiktok.ts
132826
132965
  init_auth();
132827
- import * as fs13 from "fs";
132828
- import * as path18 from "path";
132966
+ import * as fs14 from "fs";
132967
+ import * as path19 from "path";
132829
132968
  init_cli_json_snapshot();
132830
132969
  init_cli_table();
132831
132970
  async function uploadLicenseToTikTok(filePath, businessCentreType, config, verbose) {
132832
- const fileName = path18.basename(filePath);
132833
- const fileBuffer = fs13.readFileSync(filePath);
132971
+ const fileName = path19.basename(filePath);
132972
+ const fileBuffer = fs14.readFileSync(filePath);
132834
132973
  const base64Image = fileBuffer.toString("base64");
132835
132974
  const res = await apiFetch2(
132836
132975
  `${config.apiBaseUrl}/command/media-account/tiktok/Upload`,
@@ -132855,7 +132994,7 @@ async function uploadLicenseToTikTok(filePath, businessCentreType, config, verbo
132855
132994
  async function runOpenAccountTikTok(opts) {
132856
132995
  const config = loadConfig(opts.token);
132857
132996
  const bcType = opts.businessCentreType ?? "Shop";
132858
- if (!fs13.existsSync(opts.licenseFile)) {
132997
+ if (!fs14.existsSync(opts.licenseFile)) {
132859
132998
  console.error(`
132860
132999
  \u274C \u8425\u4E1A\u6267\u7167\u6587\u4EF6\u4E0D\u5B58\u5728\uFF1A${opts.licenseFile}
132861
133000
  `);
@@ -134044,10 +134183,10 @@ async function runWebsiteDiagnosisCollect(opts) {
134044
134183
 
134045
134184
  // src/commands/website-diagnosis/prepare.ts
134046
134185
  init_cli_json_snapshot();
134047
- import fs14 from "fs";
134048
- import path19 from "path";
134186
+ import fs15 from "fs";
134187
+ import path20 from "path";
134049
134188
  function readJson(filePath) {
134050
- const text3 = fs14.readFileSync(filePath, "utf8");
134189
+ const text3 = fs15.readFileSync(filePath, "utf8");
134051
134190
  try {
134052
134191
  const v = JSON.parse(text3);
134053
134192
  if (!v || typeof v !== "object" || Array.isArray(v)) {
@@ -134077,8 +134216,8 @@ function signalsFromCollectPayload(collect) {
134077
134216
  });
134078
134217
  }
134079
134218
  async function runWebsiteDiagnosisPrepare(opts) {
134080
- const collectPath = path19.resolve(opts.collectFile);
134081
- if (!fs14.existsSync(collectPath)) {
134219
+ const collectPath = path20.resolve(opts.collectFile);
134220
+ if (!fs15.existsSync(collectPath)) {
134082
134221
  console.error(`
134083
134222
  \u274C \u627E\u4E0D\u5230 collect \u6587\u4EF6\uFF1A${collectPath}
134084
134223
  `);
@@ -134087,11 +134226,11 @@ async function runWebsiteDiagnosisPrepare(opts) {
134087
134226
  const collect = readJson(collectPath);
134088
134227
  const signals = signalsFromCollectPayload(collect);
134089
134228
  const scaffold = buildDiagnosisScaffold(signals);
134090
- const outPath = path19.resolve(
134091
- opts.out ?? path19.join(path19.dirname(collectPath), "diagnosis.scaffold.json")
134229
+ const outPath = path20.resolve(
134230
+ opts.out ?? path20.join(path20.dirname(collectPath), "diagnosis.scaffold.json")
134092
134231
  );
134093
- fs14.mkdirSync(path19.dirname(outPath), { recursive: true });
134094
- fs14.writeFileSync(outPath, JSON.stringify(scaffold, null, 2), "utf8");
134232
+ fs15.mkdirSync(path20.dirname(outPath), { recursive: true });
134233
+ fs15.writeFileSync(outPath, JSON.stringify(scaffold, null, 2), "utf8");
134095
134234
  const summary = {
134096
134235
  url: signals.url,
134097
134236
  out: outPath,
@@ -134128,23 +134267,23 @@ async function runWebsiteDiagnosisPrepare(opts) {
134128
134267
  }
134129
134268
 
134130
134269
  // src/commands/website-diagnosis/render-report.ts
134131
- import fs15 from "fs";
134270
+ import fs16 from "fs";
134132
134271
  import fsPromises from "fs/promises";
134133
- import path20 from "path";
134272
+ import path21 from "path";
134134
134273
  import { fileURLToPath as fileURLToPath4 } from "url";
134135
134274
  var TEMPLATE_BASENAMES = {
134136
134275
  html: "website-diagnosis-report.html",
134137
134276
  runtime: "website-diagnosis-report.runtime.js"
134138
134277
  };
134139
134278
  function resolveSkillTemplatePath(basename13) {
134140
- const dir = path20.dirname(fileURLToPath4(import.meta.url));
134279
+ const dir = path21.dirname(fileURLToPath4(import.meta.url));
134141
134280
  const candidates = [
134142
- path20.join(dir, "skill", "report-templates", basename13),
134143
- path20.join(dir, "skill", "siluzan-ads", "report-templates", basename13),
134144
- path20.join(dir, "..", "..", "assets", "siluzan-ads", "report-templates", basename13)
134281
+ path21.join(dir, "skill", "report-templates", basename13),
134282
+ path21.join(dir, "skill", "siluzan-ads", "report-templates", basename13),
134283
+ path21.join(dir, "..", "..", "assets", "siluzan-ads", "report-templates", basename13)
134145
134284
  ];
134146
134285
  for (const p of candidates) {
134147
- if (fs15.existsSync(p)) return p;
134286
+ if (fs16.existsSync(p)) return p;
134148
134287
  }
134149
134288
  return candidates[0];
134150
134289
  }
@@ -134188,11 +134327,11 @@ function injectReportData(html, payload) {
134188
134327
  return html.replace(marker, `window.__WEBSITE_DIAGNOSIS__ = ${json};`);
134189
134328
  }
134190
134329
  async function runWebsiteDiagnosisRender(opts) {
134191
- const dataPath = path20.resolve(opts.dataFile);
134330
+ const dataPath = path21.resolve(opts.dataFile);
134192
134331
  const dataRaw = await readJsonFile(dataPath);
134193
134332
  let data = asRecord5(dataRaw);
134194
134333
  if (opts.collectFile) {
134195
- const collectRaw = await readJsonFile(path20.resolve(opts.collectFile));
134334
+ const collectRaw = await readJsonFile(path21.resolve(opts.collectFile));
134196
134335
  data = mergeCollectLighthouse(data, asRecord5(collectRaw));
134197
134336
  }
134198
134337
  const templatePath = websiteDiagnosisReportTemplatePath();
@@ -134206,14 +134345,14 @@ async function runWebsiteDiagnosisRender(opts) {
134206
134345
  `);
134207
134346
  process.exit(1);
134208
134347
  }
134209
- const outPath = path20.resolve(
134210
- opts.out ?? path20.join(path20.dirname(dataPath), "website-diagnosis-report.html")
134348
+ const outPath = path21.resolve(
134349
+ opts.out ?? path21.join(path21.dirname(dataPath), "website-diagnosis-report.html")
134211
134350
  );
134212
- const outDir = path20.dirname(outPath);
134351
+ const outDir = path21.dirname(outPath);
134213
134352
  await fsPromises.mkdir(outDir, { recursive: true });
134214
134353
  await fsPromises.writeFile(outPath, injectReportData(html, data), "utf8");
134215
134354
  const runtimeSrc = websiteDiagnosisRuntimePath();
134216
- const runtimeOut = path20.join(outDir, TEMPLATE_BASENAMES.runtime);
134355
+ const runtimeOut = path21.join(outDir, TEMPLATE_BASENAMES.runtime);
134217
134356
  let runtimeCopied = false;
134218
134357
  try {
134219
134358
  await fsPromises.copyFile(runtimeSrc, runtimeOut);
@@ -134417,8 +134556,8 @@ async function runMarketAnalysisCollect(opts) {
134417
134556
  }
134418
134557
 
134419
134558
  // src/commands/market-analysis/render-report.ts
134420
- import fs16 from "fs/promises";
134421
- import path21 from "path";
134559
+ import fs17 from "fs/promises";
134560
+ import path22 from "path";
134422
134561
 
134423
134562
  // src/commands/market-analysis/report-content.ts
134424
134563
  var SILUZAN_ECHARTS_CDN_URL = "https://staticpn.siluzan.com/assets/slz/homeCDN/echarts.min.js";
@@ -134869,10 +135008,10 @@ function resolveMarketAnalysisHtml(data) {
134869
135008
  throw new Error("JSON \u4E2D\u7F3A\u5C11 htmlContent \u6216 rawResponse \u5B57\u6BB5");
134870
135009
  }
134871
135010
  async function runMarketAnalysisRender(opts) {
134872
- const dataPath = path21.resolve(opts.dataFile);
135011
+ const dataPath = path22.resolve(opts.dataFile);
134873
135012
  let dataRaw;
134874
135013
  try {
134875
- dataRaw = JSON.parse(await fs16.readFile(dataPath, "utf8"));
135014
+ dataRaw = JSON.parse(await fs17.readFile(dataPath, "utf8"));
134876
135015
  } catch {
134877
135016
  console.error(`
134878
135017
  \u274C \u65E0\u6CD5\u89E3\u6790 JSON\uFF1A${dataPath}
@@ -134905,11 +135044,11 @@ async function runMarketAnalysisRender(opts) {
134905
135044
  );
134906
135045
  process.exit(1);
134907
135046
  }
134908
- const outPath = path21.resolve(
134909
- opts.out ?? path21.join(path21.dirname(dataPath), "market-analysis-report.html")
135047
+ const outPath = path22.resolve(
135048
+ opts.out ?? path22.join(path22.dirname(dataPath), "market-analysis-report.html")
134910
135049
  );
134911
- await fs16.mkdir(path21.dirname(outPath), { recursive: true });
134912
- await fs16.writeFile(outPath, html, "utf8");
135050
+ await fs17.mkdir(path22.dirname(outPath), { recursive: true });
135051
+ await fs17.writeFile(outPath, html, "utf8");
134913
135052
  const { stats } = contentCheck;
134914
135053
  console.log(`
134915
135054
  \u2705 \u5E02\u573A\u5206\u6790 HTML \u62A5\u544A\u5DF2\u751F\u6210\uFF1A${outPath}
@@ -134955,12 +135094,12 @@ function registerMarketAnalysisCommands(program2) {
134955
135094
 
134956
135095
  // src/commands/google-ads-diagnosis/run-collect.ts
134957
135096
  init_auth();
134958
- import fs21 from "fs/promises";
134959
- import path25 from "path";
135097
+ import fs22 from "fs/promises";
135098
+ import path26 from "path";
134960
135099
 
134961
135100
  // src/commands/google-ads-diagnosis/shared.ts
134962
- import fs17 from "fs";
134963
- import path22 from "path";
135101
+ import fs18 from "fs";
135102
+ import path23 from "path";
134964
135103
  import { fileURLToPath as fileURLToPath5 } from "url";
134965
135104
  var GOOGLE_ADS_DIAGNOSIS_TEMPLATE_BASENAME = "GoogleAdsDiagnosisReport.html";
134966
135105
  var GOOGLE_ADS_DIAGNOSIS_DATA_MARKER = "__REPORT_DATA__";
@@ -135017,15 +135156,15 @@ function buildGoogleAdsDiagnosisNarrativeChecks() {
135017
135156
  }
135018
135157
  var GOOGLE_ADS_DIAGNOSIS_NARRATIVE_CHECKS = buildGoogleAdsDiagnosisNarrativeChecks();
135019
135158
  function resolveSkillTemplatePath2(basename13) {
135020
- const dir = path22.dirname(fileURLToPath5(import.meta.url));
135159
+ const dir = path23.dirname(fileURLToPath5(import.meta.url));
135021
135160
  const candidates = [
135022
- path22.join(dir, "skill", "report-templates", basename13),
135023
- path22.join(dir, "skill", "siluzan-ads", "report-templates", basename13),
135024
- path22.join(dir, "..", "..", "assets", "siluzan-ads", "report-templates", basename13),
135025
- path22.join(dir, "..", "..", "..", "assets", "siluzan-ads", "report-templates", basename13)
135161
+ path23.join(dir, "skill", "report-templates", basename13),
135162
+ path23.join(dir, "skill", "siluzan-ads", "report-templates", basename13),
135163
+ path23.join(dir, "..", "..", "assets", "siluzan-ads", "report-templates", basename13),
135164
+ path23.join(dir, "..", "..", "..", "assets", "siluzan-ads", "report-templates", basename13)
135026
135165
  ];
135027
135166
  for (const p of candidates) {
135028
- if (fs17.existsSync(p)) return p;
135167
+ if (fs18.existsSync(p)) return p;
135029
135168
  }
135030
135169
  return candidates[0];
135031
135170
  }
@@ -135055,8 +135194,8 @@ function buildGoogleAdsDiagnosisCollectAgentHint(snapDir) {
135055
135194
  }
135056
135195
 
135057
135196
  // src/commands/google-ads-diagnosis/build-report-data.ts
135058
- import fs19 from "fs";
135059
- import path24 from "path";
135197
+ import fs20 from "fs";
135198
+ import path25 from "path";
135060
135199
 
135061
135200
  // src/commands/google-ads-diagnosis/agent-brief.ts
135062
135201
  function buildGoogleAdsDiagnosisAgentBrief(input) {
@@ -135125,8 +135264,8 @@ function buildGoogleAdsDiagnosisAgentBrief(input) {
135125
135264
 
135126
135265
  // src/commands/google-ads-diagnosis/snapshot-io.ts
135127
135266
  init_google_analysis_snapshot();
135128
- import fs18 from "fs";
135129
- import path23 from "path";
135267
+ import fs19 from "fs";
135268
+ import path24 from "path";
135130
135269
  function dateRangeManifest(start, end) {
135131
135270
  if (start && end) return { mode: "range", start, end };
135132
135271
  return null;
@@ -135137,17 +135276,17 @@ function findSectionSnapshotFile(snapDir, section, accountId, start, end) {
135137
135276
  accountId,
135138
135277
  dateRangeManifest(start, end)
135139
135278
  );
135140
- const exactPath = path23.join(snapDir, exactName);
135141
- if (fs18.existsSync(exactPath)) return exactPath;
135279
+ const exactPath = path24.join(snapDir, exactName);
135280
+ if (fs19.existsSync(exactPath)) return exactPath;
135142
135281
  if (start && end) return void 0;
135143
- const files = fs18.readdirSync(snapDir);
135144
- return files.filter((f) => f.startsWith(`${section}-${accountId}`) && f.endsWith(".json")).sort().map((f) => path23.join(snapDir, f)).at(-1);
135282
+ const files = fs19.readdirSync(snapDir);
135283
+ return files.filter((f) => f.startsWith(`${section}-${accountId}`) && f.endsWith(".json")).sort().map((f) => path24.join(snapDir, f)).at(-1);
135145
135284
  }
135146
135285
  function loadSectionSnapshot(snapDir, section, accountId, start, end) {
135147
135286
  const file = findSectionSnapshotFile(snapDir, section, accountId, start, end);
135148
- if (!file || !fs18.existsSync(file)) return null;
135287
+ if (!file || !fs19.existsSync(file)) return null;
135149
135288
  try {
135150
- return JSON.parse(fs18.readFileSync(file, "utf8"));
135289
+ return JSON.parse(fs19.readFileSync(file, "utf8"));
135151
135290
  } catch {
135152
135291
  return null;
135153
135292
  }
@@ -135604,10 +135743,10 @@ function toDailyConversionItem(row) {
135604
135743
  };
135605
135744
  }
135606
135745
  function loadCompanyNameFromSnap(snapDir) {
135607
- const file = path24.join(snapDir, "list-accounts-google.json");
135608
- if (!fs19.existsSync(file)) return void 0;
135746
+ const file = path25.join(snapDir, "list-accounts-google.json");
135747
+ if (!fs20.existsSync(file)) return void 0;
135609
135748
  try {
135610
- const parsed = JSON.parse(fs19.readFileSync(file, "utf8"));
135749
+ const parsed = JSON.parse(fs20.readFileSync(file, "utf8"));
135611
135750
  const item = parsed.items?.[0];
135612
135751
  return item?.ma?.mediaCustomerName?.trim() || item?.mag?.advertiserName?.trim() || void 0;
135613
135752
  } catch {
@@ -136264,7 +136403,7 @@ function invokeGoogleAnalysisCli(opts) {
136264
136403
  }
136265
136404
 
136266
136405
  // src/commands/google-ads-diagnosis/write-draft.ts
136267
- import fs20 from "fs/promises";
136406
+ import fs21 from "fs/promises";
136268
136407
  function asRecord10(value) {
136269
136408
  if (value && typeof value === "object" && !Array.isArray(value)) {
136270
136409
  return value;
@@ -136292,12 +136431,12 @@ function hasAgentNarrative(data) {
136292
136431
  }
136293
136432
  async function writeDiagnosisDraft(opts) {
136294
136433
  try {
136295
- const existing = JSON.parse(await fs20.readFile(opts.draftPath, "utf8"));
136434
+ const existing = JSON.parse(await fs21.readFile(opts.draftPath, "utf8"));
136296
136435
  const record2 = asRecord10(existing);
136297
136436
  if (record2 && hasAgentNarrative(record2)) return "skipped-existing-narrative";
136298
136437
  } catch {
136299
136438
  }
136300
- await fs20.writeFile(opts.draftPath, `${JSON.stringify(opts.reportData, null, 2)}
136439
+ await fs21.writeFile(opts.draftPath, `${JSON.stringify(opts.reportData, null, 2)}
136301
136440
  `, "utf8");
136302
136441
  return "written";
136303
136442
  }
@@ -136334,8 +136473,8 @@ async function runGoogleAdsDiagnosisCollect(opts) {
136334
136473
  console.error("\n\u274C collect \u987B\u540C\u65F6\u6307\u5B9A --start \u4E0E --end\u3002\n");
136335
136474
  process.exit(1);
136336
136475
  }
136337
- const snapDir = path25.resolve(opts.jsonOut);
136338
- await fs21.mkdir(snapDir, { recursive: true });
136476
+ const snapDir = path26.resolve(opts.jsonOut);
136477
+ await fs22.mkdir(snapDir, { recursive: true });
136339
136478
  let degraded = false;
136340
136479
  console.error(`
136341
136480
  \u{1F4E5} google-analysis \u62C9\u6570\uFF1A\u8D26\u6237 ${accountId}\uFF0C${opts.start} ~ ${opts.end}
@@ -136431,13 +136570,13 @@ async function runGoogleAdsDiagnosisCollect(opts) {
136431
136570
  agentBrief,
136432
136571
  agentHint: buildGoogleAdsDiagnosisCollectAgentHint(snapDir)
136433
136572
  };
136434
- const collectPath = path25.join(snapDir, GOOGLE_ADS_DIAGNOSIS_COLLECT_BASENAME);
136435
- await fs21.writeFile(collectPath, `${JSON.stringify(collectPayload, null, 2)}
136573
+ const collectPath = path26.join(snapDir, GOOGLE_ADS_DIAGNOSIS_COLLECT_BASENAME);
136574
+ await fs22.writeFile(collectPath, `${JSON.stringify(collectPayload, null, 2)}
136436
136575
  `, "utf8");
136437
- const draftPath = path25.join(snapDir, GOOGLE_ADS_DIAGNOSIS_REPORT_BASENAME);
136576
+ const draftPath = path26.join(snapDir, GOOGLE_ADS_DIAGNOSIS_REPORT_BASENAME);
136438
136577
  const draftOutcome = await writeDiagnosisDraft({ draftPath, reportData });
136439
- const digestPath = path25.join(snapDir, GOOGLE_ADS_DIAGNOSIS_DIGEST_BASENAME);
136440
- await fs21.writeFile(
136578
+ const digestPath = path26.join(snapDir, GOOGLE_ADS_DIAGNOSIS_DIGEST_BASENAME);
136579
+ await fs22.writeFile(
136441
136580
  digestPath,
136442
136581
  buildCollectDigest(reportData, {
136443
136582
  accountId,
@@ -136479,8 +136618,8 @@ async function runGoogleAdsDiagnosisCollect(opts) {
136479
136618
  }
136480
136619
 
136481
136620
  // src/commands/google-ads-diagnosis/render-report.ts
136482
- import fs22 from "fs/promises";
136483
- import path26 from "path";
136621
+ import fs23 from "fs/promises";
136622
+ import path27 from "path";
136484
136623
 
136485
136624
  // src/commands/google-ads-diagnosis/report-content.ts
136486
136625
  function asRecord11(value) {
@@ -136489,9 +136628,9 @@ function asRecord11(value) {
136489
136628
  }
136490
136629
  return null;
136491
136630
  }
136492
- function getNestedValue(root, path55) {
136631
+ function getNestedValue(root, path56) {
136493
136632
  let cur = root;
136494
- for (const key of path55) {
136633
+ for (const key of path56) {
136495
136634
  const rec = asRecord11(cur);
136496
136635
  if (!rec) return void 0;
136497
136636
  cur = rec[key];
@@ -137144,7 +137283,7 @@ function asRecord16(value) {
137144
137283
  }
137145
137284
  async function tryLoadCollectReportData(collectPath) {
137146
137285
  try {
137147
- const raw = JSON.parse(await fs22.readFile(collectPath, "utf8"));
137286
+ const raw = JSON.parse(await fs23.readFile(collectPath, "utf8"));
137148
137287
  const root = asRecord16(raw);
137149
137288
  const reportData = asRecord16(root?.reportData);
137150
137289
  return reportData ?? root;
@@ -137153,10 +137292,10 @@ async function tryLoadCollectReportData(collectPath) {
137153
137292
  }
137154
137293
  }
137155
137294
  async function runGoogleAdsDiagnosisRender(opts) {
137156
- const dataPath = path26.resolve(opts.dataFile);
137295
+ const dataPath = path27.resolve(opts.dataFile);
137157
137296
  let dataRaw;
137158
137297
  try {
137159
- dataRaw = JSON.parse(await fs22.readFile(dataPath, "utf8"));
137298
+ dataRaw = JSON.parse(await fs23.readFile(dataPath, "utf8"));
137160
137299
  } catch {
137161
137300
  console.error(`
137162
137301
  \u274C \u65E0\u6CD5\u89E3\u6790 JSON\uFF1A${dataPath}
@@ -137165,8 +137304,8 @@ async function runGoogleAdsDiagnosisRender(opts) {
137165
137304
  }
137166
137305
  let data = asRecord16(dataRaw);
137167
137306
  if (!opts.noMergeCollect) {
137168
- const collectPath = path26.resolve(
137169
- opts.collectFile ?? path26.join(path26.dirname(dataPath), GOOGLE_ADS_DIAGNOSIS_COLLECT_BASENAME)
137307
+ const collectPath = path27.resolve(
137308
+ opts.collectFile ?? path27.join(path27.dirname(dataPath), GOOGLE_ADS_DIAGNOSIS_COLLECT_BASENAME)
137170
137309
  );
137171
137310
  const collectReportData = await tryLoadCollectReportData(collectPath);
137172
137311
  if (collectReportData) {
@@ -137195,7 +137334,7 @@ async function runGoogleAdsDiagnosisRender(opts) {
137195
137334
  }
137196
137335
  }
137197
137336
  }
137198
- data = reconcileGoldAccountInReport(data, path26.dirname(dataPath));
137337
+ data = reconcileGoldAccountInReport(data, path27.dirname(dataPath));
137199
137338
  data = applyDataRenderedCopy(data);
137200
137339
  const overview = normalizeDiagnosisOverview(data);
137201
137340
  data = overview.data;
@@ -137251,7 +137390,7 @@ async function runGoogleAdsDiagnosisRender(opts) {
137251
137390
  const templatePath = googleAdsDiagnosisReportTemplatePath();
137252
137391
  let html;
137253
137392
  try {
137254
- html = await fs22.readFile(templatePath, "utf8");
137393
+ html = await fs23.readFile(templatePath, "utf8");
137255
137394
  } catch {
137256
137395
  console.error(`
137257
137396
  \u274C \u672A\u627E\u5230\u62A5\u544A\u6A21\u677F\uFF1A${templatePath}
@@ -137269,11 +137408,11 @@ async function runGoogleAdsDiagnosisRender(opts) {
137269
137408
  `);
137270
137409
  process.exit(1);
137271
137410
  }
137272
- const outPath = path26.resolve(
137273
- opts.out ?? path26.join(path26.dirname(dataPath), "google-ads-diagnosis-report.html")
137411
+ const outPath = path27.resolve(
137412
+ opts.out ?? path27.join(path27.dirname(dataPath), "google-ads-diagnosis-report.html")
137274
137413
  );
137275
- await fs22.mkdir(path26.dirname(outPath), { recursive: true });
137276
- await fs22.writeFile(outPath, rendered, "utf8");
137414
+ await fs23.mkdir(path27.dirname(outPath), { recursive: true });
137415
+ await fs23.writeFile(outPath, rendered, "utf8");
137277
137416
  console.log(`
137278
137417
  \u2705 Google \u5E7F\u544A\u8BCA\u65AD HTML \u62A5\u544A\u5DF2\u751F\u6210\uFF1A${outPath}
137279
137418
  `);
@@ -137340,8 +137479,8 @@ function registerGoogleAdsDiagnosisCommands(program2) {
137340
137479
 
137341
137480
  // src/commands/google-quarterly/run-collect.ts
137342
137481
  init_auth();
137343
- import fs25 from "fs/promises";
137344
- import path29 from "path";
137482
+ import fs26 from "fs/promises";
137483
+ import path30 from "path";
137345
137484
 
137346
137485
  // src/commands/google-quarterly/drill-country.ts
137347
137486
  init_geo_en2zh();
@@ -137848,31 +137987,31 @@ function buildSpendPlanDefaults(currentSpend) {
137848
137987
  }
137849
137988
 
137850
137989
  // src/commands/google-quarterly/region.ts
137851
- import fs23 from "fs";
137852
- import path27 from "path";
137990
+ import fs24 from "fs";
137991
+ import path28 from "path";
137853
137992
  import { fileURLToPath as fileURLToPath6 } from "url";
137854
137993
  var EAST_ASIA = /* @__PURE__ */ new Set(["\u4E2D\u56FD", "\u65E5\u672C", "\u97E9\u56FD", "\u671D\u9C9C", "\u8499\u53E4", "\u9999\u6E2F", "\u6FB3\u95E8", "\u53F0\u6E7E"]);
137855
137994
  var continentMap = null;
137856
137995
  function continentsPath() {
137857
- const dir = path27.dirname(fileURLToPath6(import.meta.url));
137996
+ const dir = path28.dirname(fileURLToPath6(import.meta.url));
137858
137997
  const candidates = [
137859
137998
  // dist/index.js → dist/skill/references/...
137860
- path27.join(dir, "skill", "references", "analytics", "geo-continents.json"),
137861
- path27.join(dir, "skill", "siluzan-ads", "references", "analytics", "geo-continents.json"),
137999
+ path28.join(dir, "skill", "references", "analytics", "geo-continents.json"),
138000
+ path28.join(dir, "skill", "siluzan-ads", "references", "analytics", "geo-continents.json"),
137862
138001
  // dist/index.js → tso-cli/assets/...
137863
- path27.join(dir, "..", "assets", "siluzan-ads", "references", "analytics", "geo-continents.json"),
138002
+ path28.join(dir, "..", "assets", "siluzan-ads", "references", "analytics", "geo-continents.json"),
137864
138003
  // src/commands/google-quarterly/region.ts → tso-cli/assets/...
137865
- path27.join(dir, "..", "..", "assets", "siluzan-ads", "references", "analytics", "geo-continents.json"),
137866
- path27.join(dir, "..", "..", "..", "assets", "siluzan-ads", "references", "analytics", "geo-continents.json")
138004
+ path28.join(dir, "..", "..", "assets", "siluzan-ads", "references", "analytics", "geo-continents.json"),
138005
+ path28.join(dir, "..", "..", "..", "assets", "siluzan-ads", "references", "analytics", "geo-continents.json")
137867
138006
  ];
137868
138007
  for (const p of candidates) {
137869
- if (fs23.existsSync(p)) return p;
138008
+ if (fs24.existsSync(p)) return p;
137870
138009
  }
137871
138010
  return candidates[0];
137872
138011
  }
137873
138012
  function loadContinentMap() {
137874
138013
  if (continentMap) return continentMap;
137875
- const raw = JSON.parse(fs23.readFileSync(continentsPath(), "utf8"));
138014
+ const raw = JSON.parse(fs24.readFileSync(continentsPath(), "utf8"));
137876
138015
  const map = {};
137877
138016
  for (const [k, v] of Object.entries(raw)) {
137878
138017
  if (k.startsWith("_")) continue;
@@ -138308,23 +138447,23 @@ function roundFacts(facts) {
138308
138447
  }
138309
138448
 
138310
138449
  // src/commands/google-quarterly/shared.ts
138311
- import fs24 from "fs";
138312
- import path28 from "path";
138450
+ import fs25 from "fs";
138451
+ import path29 from "path";
138313
138452
  import { fileURLToPath as fileURLToPath7 } from "url";
138314
138453
  var GOOGLE_QUARTERLY_COLLECT_BASENAME = "google-quarterly-collect.json";
138315
138454
  var GOOGLE_QUARTERLY_HTML_BASENAME = "google-quarterly-report.html";
138316
138455
  var GOOGLE_QUARTERLY_TEMPLATE_BASENAME = "google-quarterly-report.html";
138317
138456
  var GOOGLE_QUARTERLY_INJECT_MARKER = "window.__GOOGLE_QUARTERLY_REPORT__ = window.__GOOGLE_QUARTERLY_REPORT__ || null;";
138318
138457
  function googleQuarterlyTemplatePath() {
138319
- const dir = path28.dirname(fileURLToPath7(import.meta.url));
138458
+ const dir = path29.dirname(fileURLToPath7(import.meta.url));
138320
138459
  const candidates = [
138321
- path28.join(dir, "skill", "report-templates", GOOGLE_QUARTERLY_TEMPLATE_BASENAME),
138322
- path28.join(dir, "skill", "siluzan-ads", "report-templates", GOOGLE_QUARTERLY_TEMPLATE_BASENAME),
138323
- path28.join(dir, "..", "..", "assets", "siluzan-ads", "report-templates", GOOGLE_QUARTERLY_TEMPLATE_BASENAME),
138324
- path28.join(dir, "..", "..", "..", "assets", "siluzan-ads", "report-templates", GOOGLE_QUARTERLY_TEMPLATE_BASENAME)
138460
+ path29.join(dir, "skill", "report-templates", GOOGLE_QUARTERLY_TEMPLATE_BASENAME),
138461
+ path29.join(dir, "skill", "siluzan-ads", "report-templates", GOOGLE_QUARTERLY_TEMPLATE_BASENAME),
138462
+ path29.join(dir, "..", "..", "assets", "siluzan-ads", "report-templates", GOOGLE_QUARTERLY_TEMPLATE_BASENAME),
138463
+ path29.join(dir, "..", "..", "..", "assets", "siluzan-ads", "report-templates", GOOGLE_QUARTERLY_TEMPLATE_BASENAME)
138325
138464
  ];
138326
138465
  for (const p of candidates) {
138327
- if (fs24.existsSync(p)) return p;
138466
+ if (fs25.existsSync(p)) return p;
138328
138467
  }
138329
138468
  return candidates[2];
138330
138469
  }
@@ -138350,7 +138489,7 @@ function periodSnap(dir, account, start, end, extra = []) {
138350
138489
  }
138351
138490
  async function readInquiries(file) {
138352
138491
  if (!file?.trim()) return void 0;
138353
- const raw = await fs25.readFile(path29.resolve(file), "utf8");
138492
+ const raw = await fs26.readFile(path30.resolve(file), "utf8");
138354
138493
  return JSON.parse(raw);
138355
138494
  }
138356
138495
  async function runGoogleQuarterlyCollect(opts) {
@@ -138363,8 +138502,8 @@ async function runGoogleQuarterlyCollect(opts) {
138363
138502
  const previous = previousRange(current.start, current.end);
138364
138503
  const yoy = yearOverYearRange(current.start, current.end);
138365
138504
  const months = listMonths(current.start, current.end);
138366
- const snapDir = path29.resolve(opts.jsonOut);
138367
- await fs25.mkdir(snapDir, { recursive: true });
138505
+ const snapDir = path30.resolve(opts.jsonOut);
138506
+ await fs26.mkdir(snapDir, { recursive: true });
138368
138507
  let degraded = false;
138369
138508
  const run = (label2, start, end, sections) => {
138370
138509
  console.error(`
@@ -138492,8 +138631,8 @@ async function runGoogleQuarterlyCollect(opts) {
138492
138631
  planDefaults: facts.planDefaults
138493
138632
  }
138494
138633
  };
138495
- const outFile = path29.join(snapDir, GOOGLE_QUARTERLY_COLLECT_BASENAME);
138496
- await fs25.writeFile(outFile, JSON.stringify(payload, null, 2), "utf8");
138634
+ const outFile = path30.join(snapDir, GOOGLE_QUARTERLY_COLLECT_BASENAME);
138635
+ await fs26.writeFile(outFile, JSON.stringify(payload, null, 2), "utf8");
138497
138636
  console.log(
138498
138637
  JSON.stringify({
138499
138638
  kind: "siluzan-tso-google-quarterly-collect-summary",
@@ -138512,8 +138651,8 @@ async function runGoogleQuarterlyCollect(opts) {
138512
138651
  }
138513
138652
 
138514
138653
  // src/commands/google-quarterly/render-report.ts
138515
- import fs26 from "fs/promises";
138516
- import path31 from "path";
138654
+ import fs27 from "fs/promises";
138655
+ import path32 from "path";
138517
138656
 
138518
138657
  // src/commands/google-quarterly/report-content.ts
138519
138658
  function asRecord17(value) {
@@ -138556,9 +138695,9 @@ function mergeCollectFacts(agentData, collect) {
138556
138695
 
138557
138696
  // src/test-utils/report-echarts.ts
138558
138697
  import { readFileSync as readFileSync19 } from "fs";
138559
- import path30 from "path";
138698
+ import path31 from "path";
138560
138699
  import { fileURLToPath as fileURLToPath8 } from "url";
138561
- var TSO_CLI_ROOT = path30.join(path30.dirname(fileURLToPath8(import.meta.url)), "../..");
138700
+ var TSO_CLI_ROOT = path31.join(path31.dirname(fileURLToPath8(import.meta.url)), "../..");
138562
138701
 
138563
138702
  // src/commands/google-quarterly/render-report.ts
138564
138703
  function asRecord18(value) {
@@ -138568,22 +138707,22 @@ function asRecord18(value) {
138568
138707
  throw new Error("\u62A5\u544A JSON \u987B\u4E3A\u5BF9\u8C61");
138569
138708
  }
138570
138709
  async function runGoogleQuarterlyRender(opts) {
138571
- const dataPath = path31.resolve(opts.dataFile);
138710
+ const dataPath = path32.resolve(opts.dataFile);
138572
138711
  let agentRaw;
138573
138712
  try {
138574
- agentRaw = JSON.parse(await fs26.readFile(dataPath, "utf8"));
138713
+ agentRaw = JSON.parse(await fs27.readFile(dataPath, "utf8"));
138575
138714
  } catch {
138576
138715
  console.error(`
138577
138716
  \u274C \u65E0\u6CD5\u89E3\u6790 JSON\uFF1A${dataPath}
138578
138717
  `);
138579
138718
  process.exit(1);
138580
138719
  }
138581
- const collectPath = path31.resolve(
138582
- opts.collectFile ?? path31.join(path31.dirname(dataPath), GOOGLE_QUARTERLY_COLLECT_BASENAME)
138720
+ const collectPath = path32.resolve(
138721
+ opts.collectFile ?? path32.join(path32.dirname(dataPath), GOOGLE_QUARTERLY_COLLECT_BASENAME)
138583
138722
  );
138584
138723
  let collectRaw;
138585
138724
  try {
138586
- collectRaw = JSON.parse(await fs26.readFile(collectPath, "utf8"));
138725
+ collectRaw = JSON.parse(await fs27.readFile(collectPath, "utf8"));
138587
138726
  } catch {
138588
138727
  console.error(`
138589
138728
  \u274C \u65E0\u6CD5\u8BFB\u53D6 collect\uFF1A${collectPath}
@@ -138601,22 +138740,22 @@ async function runGoogleQuarterlyRender(opts) {
138601
138740
  const templatePath = googleQuarterlyTemplatePath();
138602
138741
  let html;
138603
138742
  try {
138604
- html = await fs26.readFile(templatePath, "utf8");
138743
+ html = await fs27.readFile(templatePath, "utf8");
138605
138744
  } catch {
138606
138745
  console.error(`
138607
138746
  \u274C \u627E\u4E0D\u5230\u6A21\u677F\uFF1A${templatePath}
138608
138747
  `);
138609
138748
  process.exit(1);
138610
138749
  }
138611
- const outPath = path31.resolve(opts.out ?? path31.join(path31.dirname(dataPath), GOOGLE_QUARTERLY_HTML_BASENAME));
138612
- await fs26.mkdir(path31.dirname(outPath), { recursive: true });
138750
+ const outPath = path32.resolve(opts.out ?? path32.join(path32.dirname(dataPath), GOOGLE_QUARTERLY_HTML_BASENAME));
138751
+ await fs27.mkdir(path32.dirname(outPath), { recursive: true });
138613
138752
  const periodChrome = reportPeriodChrome(readMetaRange(merged, "current"), readMetaRange(merged, "previous"));
138614
138753
  const outHtml = injectQuarterlyReport(html, { ...merged, periodChrome });
138615
138754
  if (!outHtml.includes("homeCDN/echarts.min.js") || /new Chart\s*\(/.test(outHtml)) {
138616
138755
  console.error("\n\u274C \u6A21\u677F\u5FC5\u987B\u4F7F\u7528 ECharts\uFF0C\u7981\u6B62 Chart.js\u3002\n");
138617
138756
  process.exit(1);
138618
138757
  }
138619
- await fs26.writeFile(outPath, outHtml, "utf8");
138758
+ await fs27.writeFile(outPath, outHtml, "utf8");
138620
138759
  console.log(`
138621
138760
  \u2705 ${periodChrome.reportTitle}\u5DF2\u751F\u6210\uFF1A${outPath}
138622
138761
  `);
@@ -138755,14 +138894,14 @@ var FACEBOOK_SECTION_ALIASES = {
138755
138894
  import { Command as Command2 } from "commander";
138756
138895
 
138757
138896
  // src/commands/facebook-analysis/render-report.ts
138758
- import fs34 from "fs";
138897
+ import fs35 from "fs";
138759
138898
  import fsPromises3 from "fs/promises";
138760
- import path41 from "path";
138899
+ import path42 from "path";
138761
138900
  import { fileURLToPath as fileURLToPath10 } from "url";
138762
138901
 
138763
138902
  // src/commands/facebook-analysis/merge-snapshot.ts
138764
- import * as fs33 from "fs/promises";
138765
- import * as path40 from "path";
138903
+ import * as fs34 from "fs/promises";
138904
+ import * as path41 from "path";
138766
138905
 
138767
138906
  // src/commands/facebook-analysis/report-metrics.ts
138768
138907
  function asRecord24(value) {
@@ -138914,7 +139053,7 @@ function hasResults(row) {
138914
139053
 
138915
139054
  // src/commands/facebook-analysis/merge-snapshot.ts
138916
139055
  async function readManifest3(snapshotDir) {
138917
- const entries = await fs33.readdir(snapshotDir);
139056
+ const entries = await fs34.readdir(snapshotDir);
138918
139057
  const manifestNames = entries.filter(
138919
139058
  (n) => n.startsWith("report-manifest") && n.endsWith(".json")
138920
139059
  );
@@ -138924,7 +139063,7 @@ async function readManifest3(snapshotDir) {
138924
139063
  ];
138925
139064
  for (const name2 of ordered) {
138926
139065
  try {
138927
- const raw = await fs33.readFile(path40.join(snapshotDir, name2), "utf8");
139066
+ const raw = await fs34.readFile(path41.join(snapshotDir, name2), "utf8");
138928
139067
  const parsed = JSON.parse(raw);
138929
139068
  if (parsed?.artifacts?.length) return parsed;
138930
139069
  } catch {
@@ -138934,7 +139073,7 @@ async function readManifest3(snapshotDir) {
138934
139073
  }
138935
139074
  async function loadSectionFile3(snapshotDir, file) {
138936
139075
  try {
138937
- const raw = await fs33.readFile(path40.join(snapshotDir, file), "utf8");
139076
+ const raw = await fs34.readFile(path41.join(snapshotDir, file), "utf8");
138938
139077
  return asRecord24(JSON.parse(raw));
138939
139078
  } catch {
138940
139079
  return null;
@@ -138978,7 +139117,7 @@ function mapAdSetRows(groups) {
138978
139117
  })).sort((a, b) => (b.spend ?? 0) - (a.spend ?? 0));
138979
139118
  }
138980
139119
  async function mergeFacebookSnapshotIntoReport(payload, snapshotDir) {
138981
- const absDir = path40.resolve(snapshotDir);
139120
+ const absDir = path41.resolve(snapshotDir);
138982
139121
  const manifest = await readManifest3(snapshotDir);
138983
139122
  if (!manifest) {
138984
139123
  throw new Error(
@@ -139585,14 +139724,14 @@ var TEMPLATE_BASENAMES2 = {
139585
139724
  };
139586
139725
  var INJECT_MARKER2 = "window.__META_PERIOD_REPORT__ = window.__META_PERIOD_REPORT__ || null;";
139587
139726
  function resolveSkillTemplatePath4(basename13) {
139588
- const dir = path41.dirname(fileURLToPath10(import.meta.url));
139727
+ const dir = path42.dirname(fileURLToPath10(import.meta.url));
139589
139728
  const candidates = [
139590
- path41.join(dir, "skill", "report-templates", basename13),
139591
- path41.join(dir, "skill", "siluzan-ads", "report-templates", basename13),
139592
- path41.join(dir, "..", "..", "assets", "siluzan-ads", "report-templates", basename13)
139729
+ path42.join(dir, "skill", "report-templates", basename13),
139730
+ path42.join(dir, "skill", "siluzan-ads", "report-templates", basename13),
139731
+ path42.join(dir, "..", "..", "assets", "siluzan-ads", "report-templates", basename13)
139593
139732
  ];
139594
139733
  for (const p of candidates) {
139595
- if (fs34.existsSync(p)) return p;
139734
+ if (fs35.existsSync(p)) return p;
139596
139735
  }
139597
139736
  return candidates[0];
139598
139737
  }
@@ -139637,7 +139776,7 @@ function injectReportData3(html, payload) {
139637
139776
  }
139638
139777
  function defaultOutPath(dataPath, format, out) {
139639
139778
  if (out?.trim()) {
139640
- const resolved = path41.resolve(out);
139779
+ const resolved = path42.resolve(out);
139641
139780
  if (format === "xlsx" && !/\.xlsx$/i.test(resolved)) {
139642
139781
  return resolved.replace(/\.(html?)$/i, ".xlsx");
139643
139782
  }
@@ -139646,14 +139785,14 @@ function defaultOutPath(dataPath, format, out) {
139646
139785
  }
139647
139786
  return resolved;
139648
139787
  }
139649
- const dir = path41.dirname(dataPath);
139650
- return path41.join(dir, format === "xlsx" ? "meta-period-report.xlsx" : "meta-period-report.html");
139788
+ const dir = path42.dirname(dataPath);
139789
+ return path42.join(dir, format === "xlsx" ? "meta-period-report.xlsx" : "meta-period-report.html");
139651
139790
  }
139652
139791
  async function runFacebookAnalysisRender(opts) {
139653
- const dataPath = path41.resolve(opts.dataFile);
139792
+ const dataPath = path42.resolve(opts.dataFile);
139654
139793
  let data = asRecord27(await readJsonFile4(dataPath));
139655
139794
  if (opts.snapshotDir?.trim()) {
139656
- data = await mergeFacebookSnapshotIntoReport(data, path41.resolve(opts.snapshotDir));
139795
+ data = await mergeFacebookSnapshotIntoReport(data, path42.resolve(opts.snapshotDir));
139657
139796
  }
139658
139797
  localizeFacebookReportLabels(data);
139659
139798
  const contentCheck = validateMetaPeriodReportContent(data);
@@ -139672,7 +139811,7 @@ async function runFacebookAnalysisRender(opts) {
139672
139811
  const written = [];
139673
139812
  for (const format of formats) {
139674
139813
  const outPath = defaultOutPath(dataPath, format, opts.out);
139675
- await fsPromises3.mkdir(path41.dirname(outPath), { recursive: true });
139814
+ await fsPromises3.mkdir(path42.dirname(outPath), { recursive: true });
139676
139815
  if (format === "xlsx") {
139677
139816
  writeMetaPeriodExcel(outPath, data);
139678
139817
  } else {
@@ -139708,14 +139847,14 @@ async function runFacebookAnalysisRender(opts) {
139708
139847
  }
139709
139848
 
139710
139849
  // src/commands/facebook-analysis/diagnosis-render-report.ts
139711
- import fs36 from "fs";
139850
+ import fs37 from "fs";
139712
139851
  import fsPromises4 from "fs/promises";
139713
- import path43 from "path";
139852
+ import path44 from "path";
139714
139853
  import { fileURLToPath as fileURLToPath11 } from "url";
139715
139854
 
139716
139855
  // src/commands/facebook-analysis/diagnosis-merge-snapshot.ts
139717
- import * as fs35 from "fs/promises";
139718
- import * as path42 from "path";
139856
+ import * as fs36 from "fs/promises";
139857
+ import * as path43 from "path";
139719
139858
  function asRecord28(value) {
139720
139859
  if (value && typeof value === "object" && !Array.isArray(value)) {
139721
139860
  return value;
@@ -139751,7 +139890,7 @@ async function readManifest4(snapshotDir, accountId) {
139751
139890
  const candidates = [];
139752
139891
  if (accountId) candidates.push(reportManifestFile(accountId));
139753
139892
  try {
139754
- const entries = await fs35.readdir(snapshotDir);
139893
+ const entries = await fs36.readdir(snapshotDir);
139755
139894
  const manifestNames = entries.filter(
139756
139895
  (n) => n.startsWith("report-manifest") && n.endsWith(".json")
139757
139896
  );
@@ -139765,7 +139904,7 @@ async function readManifest4(snapshotDir, accountId) {
139765
139904
  }
139766
139905
  for (const name2 of candidates) {
139767
139906
  try {
139768
- const raw = await fs35.readFile(path42.join(snapshotDir, name2), "utf8");
139907
+ const raw = await fs36.readFile(path43.join(snapshotDir, name2), "utf8");
139769
139908
  const parsed = JSON.parse(raw);
139770
139909
  if (Array.isArray(parsed.artifacts) && parsed.artifacts.length > 0) return parsed;
139771
139910
  } catch {
@@ -139775,14 +139914,14 @@ async function readManifest4(snapshotDir, accountId) {
139775
139914
  }
139776
139915
  async function loadSectionFile4(snapshotDir, file) {
139777
139916
  try {
139778
- const raw = await fs35.readFile(path42.join(snapshotDir, file), "utf8");
139917
+ const raw = await fs36.readFile(path43.join(snapshotDir, file), "utf8");
139779
139918
  return asRecord28(JSON.parse(raw));
139780
139919
  } catch {
139781
139920
  return null;
139782
139921
  }
139783
139922
  }
139784
139923
  async function mergeMetaDiagnosisSnapshotIntoReport(payload, snapshotDir) {
139785
- const absDir = path42.resolve(snapshotDir);
139924
+ const absDir = path43.resolve(snapshotDir);
139786
139925
  const metaAccountId = typeof asRecord28(payload.meta)?.accountId === "string" ? asRecord28(payload.meta)?.accountId : void 0;
139787
139926
  const manifest = await readManifest4(snapshotDir, metaAccountId);
139788
139927
  if (!manifest) {
@@ -140061,14 +140200,14 @@ function formatMetaDiagnosisReportErrors(result) {
140061
140200
  var TEMPLATE_BASENAME2 = "meta-account-diagnosis-report.html";
140062
140201
  var INJECT_MARKER3 = "window.__META_DIAGNOSIS_REPORT__ = window.__META_DIAGNOSIS_REPORT__ || null;";
140063
140202
  function resolveSkillTemplatePath5(basename13) {
140064
- const dir = path43.dirname(fileURLToPath11(import.meta.url));
140203
+ const dir = path44.dirname(fileURLToPath11(import.meta.url));
140065
140204
  const candidates = [
140066
- path43.join(dir, "skill", "report-templates", basename13),
140067
- path43.join(dir, "skill", "siluzan-ads", "report-templates", basename13),
140068
- path43.join(dir, "..", "..", "assets", "siluzan-ads", "report-templates", basename13)
140205
+ path44.join(dir, "skill", "report-templates", basename13),
140206
+ path44.join(dir, "skill", "siluzan-ads", "report-templates", basename13),
140207
+ path44.join(dir, "..", "..", "assets", "siluzan-ads", "report-templates", basename13)
140069
140208
  ];
140070
140209
  for (const p of candidates) {
140071
- if (fs36.existsSync(p)) return p;
140210
+ if (fs37.existsSync(p)) return p;
140072
140211
  }
140073
140212
  return candidates[0];
140074
140213
  }
@@ -140097,10 +140236,10 @@ function injectReportData4(html, payload) {
140097
140236
  return html.replace(INJECT_MARKER3, `window.__META_DIAGNOSIS_REPORT__ = ${json};`);
140098
140237
  }
140099
140238
  async function runFacebookAnalysisDiagnosisRender(opts) {
140100
- const dataPath = path43.resolve(opts.dataFile);
140239
+ const dataPath = path44.resolve(opts.dataFile);
140101
140240
  let data = asRecord30(await readJsonFile5(dataPath));
140102
140241
  if (opts.snapshotDir?.trim()) {
140103
- data = await mergeMetaDiagnosisSnapshotIntoReport(data, path43.resolve(opts.snapshotDir));
140242
+ data = await mergeMetaDiagnosisSnapshotIntoReport(data, path44.resolve(opts.snapshotDir));
140104
140243
  }
140105
140244
  const contentCheck = validateMetaDiagnosisReportContent(data);
140106
140245
  if (!contentCheck.ok) {
@@ -140126,10 +140265,10 @@ async function runFacebookAnalysisDiagnosisRender(opts) {
140126
140265
  `);
140127
140266
  process.exit(1);
140128
140267
  }
140129
- const outPath = path43.resolve(
140130
- opts.out ?? path43.join(path43.dirname(dataPath), "meta-account-diagnosis-report.html")
140268
+ const outPath = path44.resolve(
140269
+ opts.out ?? path44.join(path44.dirname(dataPath), "meta-account-diagnosis-report.html")
140131
140270
  );
140132
- const outDir = path43.dirname(outPath);
140271
+ const outDir = path44.dirname(outPath);
140133
140272
  await fsPromises4.mkdir(outDir, { recursive: true });
140134
140273
  await fsPromises4.writeFile(outPath, injectReportData4(html, data), "utf8");
140135
140274
  const { stats } = contentCheck;
@@ -140147,7 +140286,7 @@ async function runFacebookAnalysisDiagnosisRender(opts) {
140147
140286
 
140148
140287
  // src/commands/facebook-analysis/run-batch.ts
140149
140288
  init_auth();
140150
- import * as path44 from "path";
140289
+ import * as path45 from "path";
140151
140290
  import { performance as performance5 } from "perf_hooks";
140152
140291
  init_version();
140153
140292
 
@@ -140249,7 +140388,7 @@ async function runAllFacebookSections(opts) {
140249
140388
  const results = await runWithConcurrency3(tasks, concurrency);
140250
140389
  const succeeded = results.filter((r) => r.ok).length;
140251
140390
  const failed = results.length - succeeded;
140252
- const absoluteSnapshotDir = path44.resolve(opts.jsonOut);
140391
+ const absoluteSnapshotDir = path45.resolve(opts.jsonOut);
140253
140392
  const summary = {
140254
140393
  kind: "siluzan-tso-facebook-analysis-snapshot-batch",
140255
140394
  absoluteSnapshotDir,
@@ -140373,9 +140512,9 @@ function parseEffectiveStatusFilter(raw) {
140373
140512
  }
140374
140513
  return canonical.length ? canonical.join(",") : void 0;
140375
140514
  }
140376
- function facebookAdsUrl(facebookApiUrl, digits, path55) {
140515
+ function facebookAdsUrl(facebookApiUrl, digits, path56) {
140377
140516
  const base = facebookApiUrl.replace(/\/$/, "");
140378
- const p = path55.startsWith("/") ? path55 : `/${path55}`;
140517
+ const p = path56.startsWith("/") ? path56 : `/${path56}`;
140379
140518
  return `${base}/api/fb/ads/${digits}${p}`;
140380
140519
  }
140381
140520
  function facebookInsightsUrl(facebookApiUrl, digits, level) {
@@ -140412,7 +140551,7 @@ async function fetchFacebookAccount(config, digits, verbose) {
140412
140551
  verbose
140413
140552
  );
140414
140553
  }
140415
- async function fetchFacebookPagedList(config, digits, path55, opts) {
140554
+ async function fetchFacebookPagedList(config, digits, path56, opts) {
140416
140555
  const base = requireFacebookApi(config);
140417
140556
  const items3 = [];
140418
140557
  let after;
@@ -140427,7 +140566,7 @@ async function fetchFacebookPagedList(config, digits, path55, opts) {
140427
140566
  adsetId: opts.adsetId
140428
140567
  });
140429
140568
  const raw = await apiFetch2(
140430
- `${facebookAdsUrl(base, digits, path55)}?${query}`,
140569
+ `${facebookAdsUrl(base, digits, path56)}?${query}`,
140431
140570
  config,
140432
140571
  {},
140433
140572
  opts.verbose
@@ -140562,7 +140701,7 @@ async function runFacebookEntityList(opts, kind) {
140562
140701
  }
140563
140702
  const pageSize = resolveFacebookPageSize(opts.pageSize);
140564
140703
  const config = loadConfig(opts.token);
140565
- const path55 = `/${kind}`;
140704
+ const path56 = `/${kind}`;
140566
140705
  const labels = {
140567
140706
  campaigns: {
140568
140707
  section: "facebook-campaign-entities",
@@ -140575,7 +140714,7 @@ async function runFacebookEntityList(opts, kind) {
140575
140714
  const meta = labels[kind];
140576
140715
  let result;
140577
140716
  try {
140578
- result = await fetchFacebookPagedList(config, manifestId, path55, {
140717
+ result = await fetchFacebookPagedList(config, manifestId, path56, {
140579
140718
  pageSize,
140580
140719
  verbose: !!opts.verbose,
140581
140720
  effectiveStatus,
@@ -141321,7 +141460,7 @@ function endpointHintForBingSection(def) {
141321
141460
 
141322
141461
  // src/commands/bing-analysis/run-batch.ts
141323
141462
  init_auth();
141324
- import * as path45 from "path";
141463
+ import * as path46 from "path";
141325
141464
  import { performance as performance6 } from "perf_hooks";
141326
141465
  init_version();
141327
141466
  async function runWithConcurrency4(tasks, concurrency) {
@@ -141396,7 +141535,7 @@ async function runAllBingSections(opts) {
141396
141535
  const results = await runWithConcurrency4(tasks, concurrency);
141397
141536
  const succeeded = results.filter((r) => r.ok).length;
141398
141537
  const failed = results.length - succeeded;
141399
- const absoluteSnapshotDir = path45.resolve(opts.jsonOut);
141538
+ const absoluteSnapshotDir = path46.resolve(opts.jsonOut);
141400
141539
  const summary = {
141401
141540
  kind: "siluzan-tso-bing-analysis-snapshot-batch",
141402
141541
  absoluteSnapshotDir,
@@ -141740,14 +141879,14 @@ Bing \u5B98\u65B9\u6708\u6D88\u8017\uFF08${id}\uFF09
141740
141879
  import { Command as Command3 } from "commander";
141741
141880
 
141742
141881
  // src/commands/bing-analysis/render-report.ts
141743
- import fs38 from "fs";
141882
+ import fs39 from "fs";
141744
141883
  import fsPromises5 from "fs/promises";
141745
- import path47 from "path";
141884
+ import path48 from "path";
141746
141885
  import { fileURLToPath as fileURLToPath12 } from "url";
141747
141886
 
141748
141887
  // src/commands/bing-analysis/merge-snapshot.ts
141749
- import * as fs37 from "fs/promises";
141750
- import * as path46 from "path";
141888
+ import * as fs38 from "fs/promises";
141889
+ import * as path47 from "path";
141751
141890
  function asRecord32(value) {
141752
141891
  if (value && typeof value === "object" && !Array.isArray(value)) {
141753
141892
  return value;
@@ -141769,7 +141908,7 @@ function asRows2(value) {
141769
141908
  }
141770
141909
  async function loadSectionPayload(snapshotDir, file) {
141771
141910
  try {
141772
- const raw = await fs37.readFile(path46.join(snapshotDir, file), "utf8");
141911
+ const raw = await fs38.readFile(path47.join(snapshotDir, file), "utf8");
141773
141912
  return JSON.parse(raw);
141774
141913
  } catch {
141775
141914
  return null;
@@ -141779,7 +141918,7 @@ async function readManifest5(snapshotDir, accountId) {
141779
141918
  const candidates = [];
141780
141919
  if (accountId) candidates.push(reportManifestFile(accountId));
141781
141920
  try {
141782
- const entries = await fs37.readdir(snapshotDir);
141921
+ const entries = await fs38.readdir(snapshotDir);
141783
141922
  const manifestNames = entries.filter(
141784
141923
  (n) => n.startsWith("report-manifest") && n.endsWith(".json")
141785
141924
  );
@@ -141790,7 +141929,7 @@ async function readManifest5(snapshotDir, accountId) {
141790
141929
  }
141791
141930
  for (const name2 of candidates) {
141792
141931
  try {
141793
- const raw = await fs37.readFile(path46.join(snapshotDir, name2), "utf8");
141932
+ const raw = await fs38.readFile(path47.join(snapshotDir, name2), "utf8");
141794
141933
  const parsed = JSON.parse(raw);
141795
141934
  if (Array.isArray(parsed.artifacts) && parsed.artifacts.length > 0) return parsed;
141796
141935
  } catch {
@@ -141957,7 +142096,7 @@ function mapSearchTermRows(payload) {
141957
142096
  );
141958
142097
  }
141959
142098
  async function mergeBingAnalysisSnapshotIntoReport(payload, snapshotDir) {
141960
- const absDir = path46.resolve(snapshotDir);
142099
+ const absDir = path47.resolve(snapshotDir);
141961
142100
  const metaAccountId = typeof asRecord32(payload.meta)?.accountId === "string" ? asRecord32(payload.meta)?.accountId : void 0;
141962
142101
  const manifest = await readManifest5(snapshotDir, metaAccountId);
141963
142102
  if (!manifest) {
@@ -142193,14 +142332,14 @@ init_report_period();
142193
142332
  var TEMPLATE_BASENAME3 = "bing-period-report.html";
142194
142333
  var INJECT_MARKER4 = "window.__BING_PERIOD_REPORT__ = window.__BING_PERIOD_REPORT__ || null;";
142195
142334
  function resolveSkillTemplatePath6(basename13) {
142196
- const dir = path47.dirname(fileURLToPath12(import.meta.url));
142335
+ const dir = path48.dirname(fileURLToPath12(import.meta.url));
142197
142336
  const candidates = [
142198
- path47.join(dir, "skill", "report-templates", basename13),
142199
- path47.join(dir, "skill", "siluzan-ads", "report-templates", basename13),
142200
- path47.join(dir, "..", "..", "assets", "siluzan-ads", "report-templates", basename13)
142337
+ path48.join(dir, "skill", "report-templates", basename13),
142338
+ path48.join(dir, "skill", "siluzan-ads", "report-templates", basename13),
142339
+ path48.join(dir, "..", "..", "assets", "siluzan-ads", "report-templates", basename13)
142201
142340
  ];
142202
142341
  for (const p of candidates) {
142203
- if (fs38.existsSync(p)) return p;
142342
+ if (fs39.existsSync(p)) return p;
142204
142343
  }
142205
142344
  return candidates[0];
142206
142345
  }
@@ -142229,10 +142368,10 @@ function injectReportData5(html, payload) {
142229
142368
  return html.replace(INJECT_MARKER4, `window.__BING_PERIOD_REPORT__ = ${json};`);
142230
142369
  }
142231
142370
  async function runBingAnalysisRender(opts) {
142232
- const dataPath = path47.resolve(opts.dataFile);
142371
+ const dataPath = path48.resolve(opts.dataFile);
142233
142372
  let data = asRecord34(await readJsonFile6(dataPath));
142234
142373
  if (opts.snapshotDir?.trim()) {
142235
- data = await mergeBingAnalysisSnapshotIntoReport(data, path47.resolve(opts.snapshotDir));
142374
+ data = await mergeBingAnalysisSnapshotIntoReport(data, path48.resolve(opts.snapshotDir));
142236
142375
  }
142237
142376
  const contentCheck = validateBingPeriodReportContent(data);
142238
142377
  if (!contentCheck.ok) {
@@ -142258,10 +142397,10 @@ async function runBingAnalysisRender(opts) {
142258
142397
  `);
142259
142398
  process.exit(1);
142260
142399
  }
142261
- const outPath = path47.resolve(
142262
- opts.out ?? path47.join(path47.dirname(dataPath), "bing-period-report.html")
142400
+ const outPath = path48.resolve(
142401
+ opts.out ?? path48.join(path48.dirname(dataPath), "bing-period-report.html")
142263
142402
  );
142264
- const outDir = path47.dirname(outPath);
142403
+ const outDir = path48.dirname(outPath);
142265
142404
  await fsPromises5.mkdir(outDir, { recursive: true });
142266
142405
  await fsPromises5.writeFile(outPath, injectReportData5(html, data), "utf8");
142267
142406
  const { stats } = contentCheck;
@@ -142598,7 +142737,7 @@ async function fetchTikTokInterestList(config, account, verbose) {
142598
142737
 
142599
142738
  // src/commands/tiktok-analysis/run-batch.ts
142600
142739
  init_auth();
142601
- import * as path48 from "path";
142740
+ import * as path49 from "path";
142602
142741
  import { performance as performance7 } from "perf_hooks";
142603
142742
  init_version();
142604
142743
  async function runWithConcurrency5(tasks, concurrency) {
@@ -142673,7 +142812,7 @@ async function runAllTikTokSections(opts) {
142673
142812
  const results = await runWithConcurrency5(tasks, concurrency);
142674
142813
  const succeeded = results.filter((r) => r.ok).length;
142675
142814
  const failed = results.length - succeeded;
142676
- const absoluteSnapshotDir = path48.resolve(opts.jsonOut);
142815
+ const absoluteSnapshotDir = path49.resolve(opts.jsonOut);
142677
142816
  const summary = {
142678
142817
  kind: "siluzan-tso-tiktok-analysis-snapshot-batch",
142679
142818
  absoluteSnapshotDir,
@@ -142696,14 +142835,14 @@ init_auth();
142696
142835
  import { Command as Command4 } from "commander";
142697
142836
 
142698
142837
  // src/commands/tiktok-analysis/render-report.ts
142699
- import fs40 from "fs";
142838
+ import fs41 from "fs";
142700
142839
  import fsPromises6 from "fs/promises";
142701
- import path50 from "path";
142840
+ import path51 from "path";
142702
142841
  import { fileURLToPath as fileURLToPath13 } from "url";
142703
142842
 
142704
142843
  // src/commands/tiktok-analysis/merge-snapshot.ts
142705
- import * as fs39 from "fs/promises";
142706
- import * as path49 from "path";
142844
+ import * as fs40 from "fs/promises";
142845
+ import * as path50 from "path";
142707
142846
  function asRecord35(value) {
142708
142847
  if (value && typeof value === "object" && !Array.isArray(value)) {
142709
142848
  return value;
@@ -142719,7 +142858,7 @@ function round28(value) {
142719
142858
  }
142720
142859
  async function loadSectionPayload2(snapshotDir, file) {
142721
142860
  try {
142722
- const raw = await fs39.readFile(path49.join(snapshotDir, file), "utf8");
142861
+ const raw = await fs40.readFile(path50.join(snapshotDir, file), "utf8");
142723
142862
  return JSON.parse(raw);
142724
142863
  } catch {
142725
142864
  return null;
@@ -142729,7 +142868,7 @@ async function readManifest6(snapshotDir, accountId) {
142729
142868
  const candidates = [];
142730
142869
  if (accountId) candidates.push(reportManifestFile(accountId));
142731
142870
  try {
142732
- const entries = await fs39.readdir(snapshotDir);
142871
+ const entries = await fs40.readdir(snapshotDir);
142733
142872
  const manifestNames = entries.filter(
142734
142873
  (n) => n.startsWith("report-manifest") && n.endsWith(".json")
142735
142874
  );
@@ -142740,7 +142879,7 @@ async function readManifest6(snapshotDir, accountId) {
142740
142879
  }
142741
142880
  for (const name2 of candidates) {
142742
142881
  try {
142743
- const raw = await fs39.readFile(path49.join(snapshotDir, name2), "utf8");
142882
+ const raw = await fs40.readFile(path50.join(snapshotDir, name2), "utf8");
142744
142883
  const parsed = JSON.parse(raw);
142745
142884
  if (Array.isArray(parsed.artifacts) && parsed.artifacts.length > 0) return parsed;
142746
142885
  } catch {
@@ -142881,7 +143020,7 @@ function mapAudienceMerged(payload) {
142881
143020
  };
142882
143021
  }
142883
143022
  async function mergeTikTokAnalysisSnapshotIntoReport(payload, snapshotDir) {
142884
- const absDir = path49.resolve(snapshotDir);
143023
+ const absDir = path50.resolve(snapshotDir);
142885
143024
  const metaAccountId = typeof asRecord35(payload.meta)?.accountId === "string" ? asRecord35(payload.meta)?.accountId : void 0;
142886
143025
  const manifest = await readManifest6(snapshotDir, metaAccountId);
142887
143026
  if (!manifest) {
@@ -143092,14 +143231,14 @@ init_report_period();
143092
143231
  var TEMPLATE_BASENAME4 = "tiktok-period-report.html";
143093
143232
  var INJECT_MARKER5 = "window.__TIKTOK_PERIOD_REPORT__ = window.__TIKTOK_PERIOD_REPORT__ || null;";
143094
143233
  function resolveSkillTemplatePath7(basename13) {
143095
- const dir = path50.dirname(fileURLToPath13(import.meta.url));
143234
+ const dir = path51.dirname(fileURLToPath13(import.meta.url));
143096
143235
  const candidates = [
143097
- path50.join(dir, "skill", "report-templates", basename13),
143098
- path50.join(dir, "skill", "siluzan-ads", "report-templates", basename13),
143099
- path50.join(dir, "..", "..", "assets", "siluzan-ads", "report-templates", basename13)
143236
+ path51.join(dir, "skill", "report-templates", basename13),
143237
+ path51.join(dir, "skill", "siluzan-ads", "report-templates", basename13),
143238
+ path51.join(dir, "..", "..", "assets", "siluzan-ads", "report-templates", basename13)
143100
143239
  ];
143101
143240
  for (const p of candidates) {
143102
- if (fs40.existsSync(p)) return p;
143241
+ if (fs41.existsSync(p)) return p;
143103
143242
  }
143104
143243
  return candidates[0];
143105
143244
  }
@@ -143128,10 +143267,10 @@ function injectReportData6(html, payload) {
143128
143267
  return html.replace(INJECT_MARKER5, `window.__TIKTOK_PERIOD_REPORT__ = ${json};`);
143129
143268
  }
143130
143269
  async function runTikTokAnalysisRender(opts) {
143131
- const dataPath = path50.resolve(opts.dataFile);
143270
+ const dataPath = path51.resolve(opts.dataFile);
143132
143271
  let data = asRecord37(await readJsonFile7(dataPath));
143133
143272
  if (opts.snapshotDir?.trim()) {
143134
- data = await mergeTikTokAnalysisSnapshotIntoReport(data, path50.resolve(opts.snapshotDir));
143273
+ data = await mergeTikTokAnalysisSnapshotIntoReport(data, path51.resolve(opts.snapshotDir));
143135
143274
  }
143136
143275
  const contentCheck = validateTikTokPeriodReportContent(data);
143137
143276
  if (!contentCheck.ok) {
@@ -143157,10 +143296,10 @@ async function runTikTokAnalysisRender(opts) {
143157
143296
  `);
143158
143297
  process.exit(1);
143159
143298
  }
143160
- const outPath = path50.resolve(
143161
- opts.out ?? path50.join(path50.dirname(dataPath), "tiktok-period-report.html")
143299
+ const outPath = path51.resolve(
143300
+ opts.out ?? path51.join(path51.dirname(dataPath), "tiktok-period-report.html")
143162
143301
  );
143163
- const outDir = path50.dirname(outPath);
143302
+ const outDir = path51.dirname(outPath);
143164
143303
  await fsPromises6.mkdir(outDir, { recursive: true });
143165
143304
  await fsPromises6.writeFile(outPath, injectReportData6(html, data), "utf8");
143166
143305
  const { stats } = contentCheck;
@@ -144087,7 +144226,7 @@ var YANDEX_SECTION_ALIASES = {
144087
144226
 
144088
144227
  // src/commands/yandex-analysis/run-batch.ts
144089
144228
  init_auth();
144090
- import * as path52 from "path";
144229
+ import * as path53 from "path";
144091
144230
  import { performance as performance8 } from "perf_hooks";
144092
144231
  init_version();
144093
144232
 
@@ -144227,8 +144366,8 @@ function endpointHintForYandexSection(def) {
144227
144366
  }
144228
144367
 
144229
144368
  // src/commands/yandex-analysis/merge-snapshot.ts
144230
- import * as fs41 from "fs/promises";
144231
- import * as path51 from "path";
144369
+ import * as fs42 from "fs/promises";
144370
+ import * as path52 from "path";
144232
144371
  function asRecord38(value) {
144233
144372
  if (value && typeof value === "object" && !Array.isArray(value)) {
144234
144373
  return value;
@@ -144278,7 +144417,7 @@ function pickMetrics2(row, extras = []) {
144278
144417
  }
144279
144418
  async function loadSectionPayload3(snapshotDir, file) {
144280
144419
  try {
144281
- const raw = await fs41.readFile(path51.join(snapshotDir, file), "utf8");
144420
+ const raw = await fs42.readFile(path52.join(snapshotDir, file), "utf8");
144282
144421
  return JSON.parse(raw);
144283
144422
  } catch {
144284
144423
  return null;
@@ -144288,7 +144427,7 @@ async function readManifest7(snapshotDir, accountId) {
144288
144427
  const candidates = [];
144289
144428
  if (accountId) candidates.push(reportManifestFile(accountId));
144290
144429
  try {
144291
- const entries = await fs41.readdir(snapshotDir);
144430
+ const entries = await fs42.readdir(snapshotDir);
144292
144431
  for (const name2 of entries.filter(
144293
144432
  (n) => n.startsWith("report-manifest") && n.endsWith(".json")
144294
144433
  )) {
@@ -144298,7 +144437,7 @@ async function readManifest7(snapshotDir, accountId) {
144298
144437
  }
144299
144438
  for (const name2 of candidates) {
144300
144439
  try {
144301
- const raw = await fs41.readFile(path51.join(snapshotDir, name2), "utf8");
144440
+ const raw = await fs42.readFile(path52.join(snapshotDir, name2), "utf8");
144302
144441
  const parsed = JSON.parse(raw);
144303
144442
  if (Array.isArray(parsed.artifacts) && parsed.artifacts.length > 0) return parsed;
144304
144443
  } catch {
@@ -144457,7 +144596,7 @@ function mapDeviceRows2(payload) {
144457
144596
  return asItems(payload).map((r) => pickMetrics2(r, ["device", "network"]));
144458
144597
  }
144459
144598
  async function mergeYandexAnalysisSnapshotIntoReport(payload, snapshotDir) {
144460
- const absDir = path51.resolve(snapshotDir);
144599
+ const absDir = path52.resolve(snapshotDir);
144461
144600
  const metaAccountId = typeof asRecord38(payload.meta)?.accountId === "string" ? asRecord38(payload.meta)?.accountId : void 0;
144462
144601
  const manifest = await readManifest7(snapshotDir, metaAccountId);
144463
144602
  if (!manifest) {
@@ -144643,7 +144782,7 @@ async function runAllYandexSections(opts) {
144643
144782
  const results = await runWithConcurrency6(tasks, concurrency);
144644
144783
  const succeeded = results.filter((r) => r.ok).length;
144645
144784
  const failed = results.length - succeeded;
144646
- const absoluteSnapshotDir = path52.resolve(opts.jsonOut);
144785
+ const absoluteSnapshotDir = path53.resolve(opts.jsonOut);
144647
144786
  const summary = {
144648
144787
  kind: "siluzan-tso-yandex-analysis-snapshot-batch",
144649
144788
  absoluteSnapshotDir,
@@ -144661,9 +144800,9 @@ async function runAllYandexSections(opts) {
144661
144800
  }
144662
144801
 
144663
144802
  // src/commands/yandex-analysis/render-report.ts
144664
- import fs42 from "fs";
144803
+ import fs43 from "fs";
144665
144804
  import fsPromises7 from "fs/promises";
144666
- import path53 from "path";
144805
+ import path54 from "path";
144667
144806
  import { fileURLToPath as fileURLToPath14 } from "url";
144668
144807
 
144669
144808
  // src/commands/yandex-analysis/report-content.ts
@@ -144868,14 +145007,14 @@ init_report_period();
144868
145007
  var TEMPLATE_BASENAME5 = "yandex-period-report.html";
144869
145008
  var INJECT_MARKER6 = "window.__YANDEX_PERIOD_REPORT__ = window.__YANDEX_PERIOD_REPORT__ || null;";
144870
145009
  function resolveSkillTemplatePath8(basename13) {
144871
- const dir = path53.dirname(fileURLToPath14(import.meta.url));
145010
+ const dir = path54.dirname(fileURLToPath14(import.meta.url));
144872
145011
  const candidates = [
144873
- path53.join(dir, "skill", "report-templates", basename13),
144874
- path53.join(dir, "skill", "siluzan-ads", "report-templates", basename13),
144875
- path53.join(dir, "..", "..", "assets", "siluzan-ads", "report-templates", basename13)
145012
+ path54.join(dir, "skill", "report-templates", basename13),
145013
+ path54.join(dir, "skill", "siluzan-ads", "report-templates", basename13),
145014
+ path54.join(dir, "..", "..", "assets", "siluzan-ads", "report-templates", basename13)
144876
145015
  ];
144877
145016
  for (const p of candidates) {
144878
- if (fs42.existsSync(p)) return p;
145017
+ if (fs43.existsSync(p)) return p;
144879
145018
  }
144880
145019
  return candidates[0];
144881
145020
  }
@@ -144917,10 +145056,10 @@ function injectReportData7(html, payload) {
144917
145056
  return html.replace(INJECT_MARKER6, `window.__YANDEX_PERIOD_REPORT__ = ${json};`);
144918
145057
  }
144919
145058
  async function runYandexAnalysisRender(opts) {
144920
- const dataPath = path53.resolve(opts.dataFile);
145059
+ const dataPath = path54.resolve(opts.dataFile);
144921
145060
  let data = asRecord40(await readJsonFile8(dataPath));
144922
145061
  if (opts.snapshotDir?.trim()) {
144923
- data = await mergeYandexAnalysisSnapshotIntoReport(data, path53.resolve(opts.snapshotDir));
145062
+ data = await mergeYandexAnalysisSnapshotIntoReport(data, path54.resolve(opts.snapshotDir));
144924
145063
  }
144925
145064
  data = refreshWeeklyFromDaily(data);
144926
145065
  const contentCheck = validateYandexPeriodReportContent(data);
@@ -144945,10 +145084,10 @@ async function runYandexAnalysisRender(opts) {
144945
145084
  `);
144946
145085
  process.exit(1);
144947
145086
  }
144948
- const outPath = path53.resolve(
144949
- opts.out ?? path53.join(path53.dirname(dataPath), "yandex-period-report.html")
145087
+ const outPath = path54.resolve(
145088
+ opts.out ?? path54.join(path54.dirname(dataPath), "yandex-period-report.html")
144950
145089
  );
144951
- const outDir = path53.dirname(outPath);
145090
+ const outDir = path54.dirname(outPath);
144952
145091
  await fsPromises7.mkdir(outDir, { recursive: true });
144953
145092
  await fsPromises7.writeFile(outPath, injectReportData7(html, data), "utf8");
144954
145093
  const { stats } = contentCheck;
@@ -145049,13 +145188,13 @@ function optionalId(raw) {
145049
145188
  function insightsLevelToPath(level) {
145050
145189
  return level;
145051
145190
  }
145052
- function assertYandexAnalysisQuery(path55, query) {
145053
- const caps = YANDEX_PATH_CAPS[path55];
145191
+ function assertYandexAnalysisQuery(path56, query) {
145192
+ const caps = YANDEX_PATH_CAPS[path56];
145054
145193
  if (query.timeIncrement != null && !caps.timeIncrement) {
145055
- throw new Error(`--time-increment \u4E0D\u80FD\u7528\u4E8E /${path55}\uFF08\u4EC5 campaigns / adgroups / ads\uFF09\u3002`);
145194
+ throw new Error(`--time-increment \u4E0D\u80FD\u7528\u4E8E /${path56}\uFF08\u4EC5 campaigns / adgroups / ads\uFF09\u3002`);
145056
145195
  }
145057
145196
  if (query.network != null && !caps.network) {
145058
- throw new Error(`--network \u4E0D\u80FD\u7528\u4E8E /${path55}\uFF08\u4EC5 campaigns / geo / devices\uFF09\u3002`);
145197
+ throw new Error(`--network \u4E0D\u80FD\u7528\u4E8E /${path56}\uFF08\u4EC5 campaigns / geo / devices\uFF09\u3002`);
145059
145198
  }
145060
145199
  if (query.status != null && !caps.status) {
145061
145200
  throw new Error(`--status \u4EC5\u7528\u4E8E /ads\uFF08\u62D2\u5BA1/\u5BA1\u6838\u6001\u8D70\u7ED3\u6784\u67E5\u8BE2\uFF0C\u884C\u5185 spend \u4E3A 0\uFF09\u3002`);
@@ -145072,12 +145211,12 @@ function assertYandexAnalysisQuery(path55, query) {
145072
145211
  );
145073
145212
  }
145074
145213
  if (query.limit != null && !caps.limit) {
145075
- throw new Error(`--limit \u4E0D\u80FD\u7528\u4E8E /${path55}\uFF08overview / daily / devices \u65E0\u6B64\u53C2\u6570\uFF09\u3002`);
145214
+ throw new Error(`--limit \u4E0D\u80FD\u7528\u4E8E /${path56}\uFF08overview / daily / devices \u65E0\u6B64\u53C2\u6570\uFF09\u3002`);
145076
145215
  }
145077
145216
  }
145078
- function buildYandexAnalysisSearchParams(path55, query) {
145079
- assertYandexAnalysisQuery(path55, query);
145080
- const caps = YANDEX_PATH_CAPS[path55];
145217
+ function buildYandexAnalysisSearchParams(path56, query) {
145218
+ assertYandexAnalysisQuery(path56, query);
145219
+ const caps = YANDEX_PATH_CAPS[path56];
145081
145220
  const params = new URLSearchParams({
145082
145221
  account: query.account,
145083
145222
  start: query.start,
@@ -145103,10 +145242,10 @@ function buildYandexAnalysisSearchParams(path55, query) {
145103
145242
  }
145104
145243
  return params;
145105
145244
  }
145106
- async function fetchYandexAnalysisPath(config, path55, query, verbose) {
145245
+ async function fetchYandexAnalysisPath(config, path56, query, verbose) {
145107
145246
  const yandexApiUrl = requireYandexApi(config);
145108
- const params = buildYandexAnalysisSearchParams(path55, query);
145109
- const url = analysisUrl(yandexApiUrl, path55, params.toString());
145247
+ const params = buildYandexAnalysisSearchParams(path56, query);
145248
+ const url = analysisUrl(yandexApiUrl, path56, params.toString());
145110
145249
  return apiFetch2(url, config, {}, verbose);
145111
145250
  }
145112
145251
  function unwrapYandexItems(raw) {
@@ -145170,13 +145309,13 @@ ${args.title}
145170
145309
  console.log(` ${args.footnote}
145171
145310
  `);
145172
145311
  }
145173
- async function fetchPath(opts, path55, extra) {
145312
+ async function fetchPath(opts, path56, extra) {
145174
145313
  const accountId = assertYandexAccountId(opts.account);
145175
145314
  const { startDate, endDate } = resolveRange(opts.start, opts.end);
145176
145315
  const config = loadConfig(opts.token);
145177
145316
  const raw = await fetchYandexAnalysisPath(
145178
145317
  config,
145179
- path55,
145318
+ path56,
145180
145319
  {
145181
145320
  account: accountId,
145182
145321
  start: startDate,
@@ -145667,9 +145806,9 @@ function interceptAgentCompatArgv(argv) {
145667
145806
  installProcessHandlers();
145668
145807
  function getVersion() {
145669
145808
  try {
145670
- const __dirname3 = path54.dirname(fileURLToPath15(import.meta.url));
145671
- const pkgPath = path54.join(__dirname3, "..", "package.json");
145672
- const pkg = JSON.parse(fs43.readFileSync(pkgPath, "utf8"));
145809
+ const __dirname3 = path55.dirname(fileURLToPath15(import.meta.url));
145810
+ const pkgPath = path55.join(__dirname3, "..", "package.json");
145811
+ const pkg = JSON.parse(fs44.readFileSync(pkgPath, "utf8"));
145673
145812
  return pkg.version ?? "0.0.0";
145674
145813
  } catch {
145675
145814
  return "0.0.0";