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

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -258,9 +258,9 @@ var require_semver = __commonJS({
258
258
  } else {
259
259
  this.prerelease = m[4].split(".").map((id) => {
260
260
  if (/^[0-9]+$/.test(id)) {
261
- const num5 = +id;
262
- if (num5 >= 0 && num5 < MAX_SAFE_INTEGER) {
263
- return num5;
261
+ const num6 = +id;
262
+ if (num6 >= 0 && num6 < MAX_SAFE_INTEGER) {
263
+ return num6;
264
264
  }
265
265
  }
266
266
  return id;
@@ -107794,6 +107794,57 @@ async function fetchBingJson(config, url, verbose) {
107794
107794
  // src/commands/report-bing-analysis/run.ts
107795
107795
  init_auth();
107796
107796
  init_balance();
107797
+
107798
+ // src/commands/report-bing-analysis/normalize-bing-overview.ts
107799
+ function num(v) {
107800
+ return typeof v === "number" && Number.isFinite(v) ? v : void 0;
107801
+ }
107802
+ function roundMoney(v) {
107803
+ return Math.round(v * 100) / 100;
107804
+ }
107805
+ function countInclusiveCalendarDays(startDate, endDate) {
107806
+ const parts = (ymd) => ymd.trim().split("-").map((x) => parseInt(x, 10));
107807
+ const sp = parts(startDate);
107808
+ const ep = parts(endDate);
107809
+ if (sp.length !== 3 || ep.length !== 3 || sp.some((n) => !Number.isFinite(n)) || ep.some((n) => !Number.isFinite(n))) {
107810
+ return 0;
107811
+ }
107812
+ const s = new Date(sp[0], sp[1] - 1, sp[2]);
107813
+ const e = new Date(ep[0], ep[1] - 1, ep[2]);
107814
+ if (Number.isNaN(s.getTime()) || Number.isNaN(e.getTime()) || s > e) return 0;
107815
+ const msPerDay = 24 * 60 * 60 * 1e3;
107816
+ return Math.floor((e.getTime() - s.getTime()) / msPerDay) + 1;
107817
+ }
107818
+ function periodSpend(period) {
107819
+ if (!period || typeof period !== "object") return void 0;
107820
+ return num(period.spend);
107821
+ }
107822
+ function enrichBingOverviewDerivedMetrics(record, startDate, endDate) {
107823
+ const out = { ...record };
107824
+ const currentSpend = periodSpend(out.currentPeriod);
107825
+ let totalCost = num(out.totalCost);
107826
+ if (totalCost == null || totalCost <= 0) {
107827
+ if (currentSpend != null && currentSpend > 0) {
107828
+ out.totalCost = currentSpend;
107829
+ totalCost = currentSpend;
107830
+ }
107831
+ }
107832
+ let activeDays = num(out.activeDays);
107833
+ if (activeDays == null || activeDays <= 0) {
107834
+ const calendarDays = countInclusiveCalendarDays(startDate, endDate);
107835
+ if (calendarDays > 0) {
107836
+ out.activeDays = calendarDays;
107837
+ activeDays = calendarDays;
107838
+ }
107839
+ }
107840
+ const averageDailyCost = num(out.averageDailyCost);
107841
+ if ((averageDailyCost == null || averageDailyCost <= 0) && totalCost != null && totalCost > 0 && activeDays != null && activeDays > 0) {
107842
+ out.averageDailyCost = roundMoney(totalCost / activeDays);
107843
+ }
107844
+ return out;
107845
+ }
107846
+
107847
+ // src/commands/report-bing-analysis/run.ts
107797
107848
  async function runReportBingOverview(opts) {
107798
107849
  const id = assertBingAccountId(opts.account);
107799
107850
  const { startDate, endDate } = resolveBingDateRange(opts.start, opts.end);
@@ -107802,13 +107853,17 @@ async function runReportBingOverview(opts) {
107802
107853
  const url = reportingUrl2(config, id, "OverviewSectionData", params.toString());
107803
107854
  try {
107804
107855
  const raw = await fetchBingJson(config, url, !!opts.verbose);
107805
- const data = raw && typeof raw === "object" && !Array.isArray(raw) ? await enrichOverviewRecordWithRealtimeBalance(
107806
- "BingV2",
107807
- id,
107808
- config,
107809
- raw,
107810
- opts.verbose
107811
- ) : raw;
107856
+ let data = raw;
107857
+ if (raw && typeof raw === "object" && !Array.isArray(raw)) {
107858
+ const withBalance = await enrichOverviewRecordWithRealtimeBalance(
107859
+ "BingV2",
107860
+ id,
107861
+ config,
107862
+ raw,
107863
+ opts.verbose
107864
+ );
107865
+ data = enrichBingOverviewDerivedMetrics(withBalance, startDate, endDate);
107866
+ }
107812
107867
  if (opts.jsonOut) {
107813
107868
  await emitBingSnapshot(opts, "bing-overview", id, startDate, endDate, data);
107814
107869
  return;
@@ -120851,6 +120906,309 @@ async function runAccountMe(opts) {
120851
120906
  }
120852
120907
  }
120853
120908
 
120909
+ // src/commands/account-manage/google-access-permission.ts
120910
+ init_dist();
120911
+ init_auth();
120912
+ init_cli_json_snapshot();
120913
+
120914
+ // src/commands/account-manage/google-mcc.ts
120915
+ init_auth();
120916
+ init_cli_json_snapshot();
120917
+ function parseMccManagerIds(input) {
120918
+ const normalized = input.replace(/[,,;、]/g, ";");
120919
+ return normalized.split(";").map((part) => {
120920
+ const digits = part.replace(/\D/g, "");
120921
+ return digits ? +digits : NaN;
120922
+ }).filter((n) => Number.isFinite(n) && n > 0);
120923
+ }
120924
+ var MCC_AGENT_TYPES = ["ChengGongYi", "SiGeUS", "SiGeCN", "Dee"];
120925
+ function googleGatewayBase(config) {
120926
+ const raw = (config.googleApiUrl ?? "").replace(/\/$/, "");
120927
+ if (!raw) {
120928
+ console.error("\n\u274C Google \u7F51\u5173\u5730\u5740\u672A\u80FD\u63A8\u5BFC\uFF0C\u8BF7\u68C0\u67E5 tsoApiBaseUrl \u914D\u7F6E\u3002\n");
120929
+ process.exit(1);
120930
+ }
120931
+ const err = validateBaseUrl(raw);
120932
+ if (err) {
120933
+ console.error(`
120934
+ \u274C googleApiUrl \u4E0D\u5408\u6CD5\uFF1A${err}
120935
+ `);
120936
+ process.exit(1);
120937
+ }
120938
+ return raw;
120939
+ }
120940
+ function mccResponseHasSuccess(res, managerId) {
120941
+ if (!Array.isArray(res)) return false;
120942
+ return res.some((item) => String(item) === String(managerId));
120943
+ }
120944
+ async function runAccountMccBind(opts) {
120945
+ const config = loadConfig(opts.token);
120946
+ const base = googleGatewayBase(config);
120947
+ const managerIds = parseMccManagerIds(opts.mcc);
120948
+ if (managerIds.length === 0) {
120949
+ console.error("\n\u274C --mcc \u4E2D\u672A\u89E3\u6790\u51FA\u6709\u6548\u7684\u6570\u5B57\u578B MCC \u5BA2\u6237 ID\n");
120950
+ process.exit(1);
120951
+ }
120952
+ if (opts.customers.length === 0) {
120953
+ console.error("\n\u274C \u8BF7\u901A\u8FC7 --customers \u6307\u5B9A\u81F3\u5C11\u4E00\u4E2A Google \u5B50\u8D26\u6237 mediaCustomerId\n");
120954
+ process.exit(1);
120955
+ }
120956
+ const body = { agentTypes: [...MCC_AGENT_TYPES], managerIds };
120957
+ const results = [];
120958
+ for (const customerId of opts.customers) {
120959
+ const url = `${base}/command/media-account/${customerId}/CustomerLinks`;
120960
+ try {
120961
+ const response = await apiFetch2(
120962
+ url,
120963
+ config,
120964
+ { method: "POST", body: JSON.stringify(body) },
120965
+ opts.verbose
120966
+ );
120967
+ results.push({ customerId, response });
120968
+ } catch (err) {
120969
+ console.error(
120970
+ `
120971
+ \u274C \u8D26\u6237 ${customerId} MCC \u7ED1\u5B9A\u8BF7\u6C42\u5931\u8D25\uFF1A${err instanceof Error ? err.message : String(err)}
120972
+ `
120973
+ );
120974
+ process.exit(1);
120975
+ }
120976
+ }
120977
+ if (await emitCliJsonOrSnapshot(opts, {
120978
+ section: "account-mcc-bind",
120979
+ commandLabel: "account mcc-bind",
120980
+ payload: results,
120981
+ idSuffix: opts.mcc
120982
+ })) {
120983
+ return;
120984
+ }
120985
+ let hasFailure = false;
120986
+ for (const { customerId, response } of results) {
120987
+ const failed = managerIds.filter((mid) => !mccResponseHasSuccess(response, mid));
120988
+ if (failed.length > 0) {
120989
+ hasFailure = true;
120990
+ console.error(` \u274C \u8D26\u6237 ${customerId}\uFF1A\u4EE5\u4E0B MCC \u672A\u5728\u6210\u529F\u5217\u8868\u4E2D \u2192 ${failed.join("\uFF0C")}`);
120991
+ } else {
120992
+ console.log(
120993
+ ` \u2705 \u8D26\u6237 ${customerId}\uFF1A\u5DF2\u5411 ${managerIds.join("\u3001")} \u53D1\u8D77\u7ED1\u5B9A\uFF08\u63A5\u53E3\u8FD4\u56DE\u6210\u529F\uFF09`
120994
+ );
120995
+ }
120996
+ }
120997
+ console.log(
120998
+ hasFailure ? "\n\u26A0\uFE0F \u90E8\u5206 MCC \u7ED1\u5B9A\u53EF\u80FD\u672A\u751F\u6548\uFF0C\u8BF7\u7ED3\u5408\u7F51\u9875\u6216 --json-out \u6838\u5BF9\u3002\n" : "\n\u2705 MCC \u7ED1\u5B9A\u6D41\u7A0B\u5DF2\u5B8C\u6210\u3002\n"
120999
+ );
121000
+ }
121001
+ async function runAccountMccUnbind(opts) {
121002
+ const config = loadConfig(opts.token);
121003
+ const base = googleGatewayBase(config);
121004
+ const managerIds = parseMccManagerIds(opts.mcc);
121005
+ if (managerIds.length === 0) {
121006
+ console.error("\n\u274C --mcc \u4E2D\u672A\u89E3\u6790\u51FA\u6709\u6548\u7684\u6570\u5B57\u578B MCC \u5BA2\u6237 ID\n");
121007
+ process.exit(1);
121008
+ }
121009
+ if (opts.customers.length === 0) {
121010
+ console.error("\n\u274C \u8BF7\u901A\u8FC7 --customers \u6307\u5B9A\u81F3\u5C11\u4E00\u4E2A Google \u5B50\u8D26\u6237 mediaCustomerId\n");
121011
+ process.exit(1);
121012
+ }
121013
+ const results = [];
121014
+ for (const customerId of opts.customers) {
121015
+ const url = `${base}/command/media-account/${customerId}/CustomerUnlinks`;
121016
+ try {
121017
+ const response = await apiFetch2(
121018
+ url,
121019
+ config,
121020
+ { method: "POST", body: JSON.stringify(managerIds) },
121021
+ opts.verbose
121022
+ );
121023
+ results.push({ customerId, response });
121024
+ } catch (err) {
121025
+ console.error(
121026
+ `
121027
+ \u274C \u8D26\u6237 ${customerId} MCC \u89E3\u7ED1\u8BF7\u6C42\u5931\u8D25\uFF1A${err instanceof Error ? err.message : String(err)}
121028
+ `
121029
+ );
121030
+ process.exit(1);
121031
+ }
121032
+ }
121033
+ if (await emitCliJsonOrSnapshot(opts, {
121034
+ section: "account-mcc-unbind",
121035
+ commandLabel: "account mcc-unbind",
121036
+ payload: results,
121037
+ idSuffix: opts.mcc
121038
+ })) {
121039
+ return;
121040
+ }
121041
+ let hasFailure = false;
121042
+ for (const { customerId, response } of results) {
121043
+ const failed = managerIds.filter((mid) => !mccResponseHasSuccess(response, mid));
121044
+ if (failed.length > 0) {
121045
+ hasFailure = true;
121046
+ console.error(` \u274C \u8D26\u6237 ${customerId}\uFF1A\u4EE5\u4E0B MCC \u672A\u5728\u6210\u529F\u89E3\u7ED1\u5217\u8868\u4E2D \u2192 ${failed.join("\uFF0C")}`);
121047
+ } else {
121048
+ console.log(
121049
+ ` \u2705 \u8D26\u6237 ${customerId}\uFF1A\u5DF2\u5411 ${managerIds.join("\u3001")} \u53D1\u8D77\u89E3\u7ED1\uFF08\u63A5\u53E3\u8FD4\u56DE\u6210\u529F\uFF09`
121050
+ );
121051
+ }
121052
+ }
121053
+ console.log(
121054
+ hasFailure ? "\n\u26A0\uFE0F \u90E8\u5206 MCC \u89E3\u7ED1\u53EF\u80FD\u672A\u751F\u6548\uFF0C\u8BF7\u7ED3\u5408\u7F51\u9875\u6216 --json-out \u6838\u5BF9\u3002\n" : "\n\u2705 MCC \u89E3\u7ED1\u6D41\u7A0B\u5DF2\u5B8C\u6210\u3002\n"
121055
+ );
121056
+ }
121057
+
121058
+ // src/commands/account-manage/google-access-permission.ts
121059
+ function assertGoogleMediaCustomerId(raw) {
121060
+ const id = raw.trim();
121061
+ if (!id) {
121062
+ console.error("\n\u274C --account \u4E0D\u80FD\u4E3A\u7A7A\u3002\n");
121063
+ process.exit(1);
121064
+ }
121065
+ if (!/^\d+$/.test(id)) {
121066
+ console.error("\n\u274C --account \u987B\u4E3A Google mediaCustomerId\uFF08\u7EAF\u6570\u5B57\uFF0C\u4E0E list-accounts -m Google \u4E00\u81F4\uFF09\u3002\n");
121067
+ process.exit(1);
121068
+ }
121069
+ return id;
121070
+ }
121071
+ function parseCustomerAccessPermissionBody(text) {
121072
+ const trimmed = text.trim();
121073
+ if (!trimmed) return null;
121074
+ try {
121075
+ const parsed = JSON.parse(trimmed);
121076
+ if (typeof parsed === "boolean") return parsed;
121077
+ if (typeof parsed === "string") return parsed;
121078
+ } catch {
121079
+ }
121080
+ return trimmed;
121081
+ }
121082
+ function interpretCustomerAccessPermission(mediaCustomerId, httpStatus, rawBody) {
121083
+ const base = {
121084
+ mediaCustomerId,
121085
+ httpStatus,
121086
+ rawBody
121087
+ };
121088
+ if (httpStatus === 200) {
121089
+ if (rawBody === true) {
121090
+ return {
121091
+ ...base,
121092
+ status: "accessible",
121093
+ accessible: true,
121094
+ message: "\u5F53\u524D\u4E1D\u8DEF\u8D5E\u8D26\u53F7\u53EF\u8BBF\u95EE\u8BE5 Google \u5E7F\u544A\u8D26\u6237\u3002"
121095
+ };
121096
+ }
121097
+ if (rawBody === false) {
121098
+ return {
121099
+ ...base,
121100
+ status: "reauth_required",
121101
+ accessible: false,
121102
+ message: "\u8D26\u6237\u5DF2\u7ED1\u5B9A\u4F46 Google OAuth \u4E0D\u53EF\u7528\uFF0C\u9700\u91CD\u65B0\u6388\u6743\u3002",
121103
+ hint: "list-accounts --json-out \u53D6 ma.entityId \u540E\u6267\u884C\uFF1Asiluzan-tso account reauth -m Google --id <entityId>"
121104
+ };
121105
+ }
121106
+ return {
121107
+ ...base,
121108
+ status: "unknown",
121109
+ accessible: false,
121110
+ message: `HTTP 200 \u4F46\u54CD\u5E94\u4F53\u975E\u5E38\u89C4\uFF08${String(rawBody)}\uFF09\uFF0C\u8BF7\u7528 --verbose \u67E5\u770B\u539F\u59CB\u54CD\u5E94\u3002`
121111
+ };
121112
+ }
121113
+ if (httpStatus === 403) {
121114
+ const text = typeof rawBody === "string" ? rawBody : rawBody == null ? "" : String(rawBody);
121115
+ if (text.includes("token\u4E0D\u80FD\u4E3A\u7A7A") || text.toLowerCase().includes("token") && text.includes("\u7A7A")) {
121116
+ return {
121117
+ ...base,
121118
+ status: "google_not_bound",
121119
+ accessible: false,
121120
+ message: "\u5F53\u524D\u4E1D\u8DEF\u8D5E\u8D26\u53F7\u672A\u7ED1\u5B9A Google \u5A92\u4F53\u6388\u6743\u3002",
121121
+ hint: "\u6267\u884C siluzan-tso account auth -m Google \u5B8C\u6210 OAuth \u7ED1\u5B9A\u3002"
121122
+ };
121123
+ }
121124
+ return {
121125
+ ...base,
121126
+ status: "no_permission",
121127
+ accessible: false,
121128
+ message: "\u5F53\u524D\u4E1D\u8DEF\u8D5E\u8D26\u53F7\u5BF9\u8BE5 Google \u5E7F\u544A\u8D26\u6237\u65E0\u8BBF\u95EE\u6743\u9650\uFF1B\u901A\u5E38\u8868\u793A\u8BE5\u8D26\u6237\u4E0D\u5728\u672C\u8D26\u53F7\u4E0B\uFF08\u6216\u672A\u5206\u4EAB\u7ED9\u4F60\uFF09\u3002",
121129
+ hint: "\u8BF7\u786E\u8BA4 mediaCustomerId \u662F\u5426\u6B63\u786E\uFF0C\u6216\u5207\u6362\u5230\u62E5\u6709\u8BE5\u8D26\u6237\u7684\u4E1D\u8DEF\u8D5E\u8D26\u53F7\u767B\u5F55\uFF08send-login-code + login\uFF09\u3002"
121130
+ };
121131
+ }
121132
+ if (httpStatus === 401) {
121133
+ return {
121134
+ ...base,
121135
+ status: "siluzan_token_invalid",
121136
+ accessible: false,
121137
+ message: "\u4E1D\u8DEF\u8D5E\u767B\u5F55\u51ED\u636E\u65E0\u6548\u6216\u5DF2\u8FC7\u671F\u3002",
121138
+ hint: "\u6267\u884C send-login-code + login --phone --code \u91CD\u65B0\u767B\u5F55\uFF0C\u6216 siluzan-tso config show \u68C0\u67E5 Token\u3002"
121139
+ };
121140
+ }
121141
+ return {
121142
+ ...base,
121143
+ status: "unknown",
121144
+ accessible: false,
121145
+ message: `\u672A\u9884\u671F\u7684 HTTP ${httpStatus}\uFF1B\u8BF7\u7528 --verbose \u67E5\u770B\u539F\u59CB\u54CD\u5E94\u3002`
121146
+ };
121147
+ }
121148
+ function buildCustomerAccessPermissionUrl(googleApiUrl, mediaCustomerId) {
121149
+ const base = googleApiUrl.replace(/\/$/, "");
121150
+ return `${base}/query/media-account/${mediaCustomerId}/customer-access-permission`;
121151
+ }
121152
+ async function fetchGoogleCustomerAccessPermission(config, mediaCustomerId, verbose) {
121153
+ await ensureDataPermission(config);
121154
+ const url = buildCustomerAccessPermissionUrl(googleGatewayBase(config), mediaCustomerId);
121155
+ const res = await rawRequest(url, {
121156
+ method: "GET",
121157
+ headers: {
121158
+ "Content-Type": "application/json",
121159
+ "Accept-Language": "zh-CN",
121160
+ Accept: "application/json",
121161
+ ...buildSiluzanAuthHeaders(config),
121162
+ Datapermission: config.dataPermission ?? ""
121163
+ }
121164
+ });
121165
+ if (verbose && res.text) {
121166
+ console.error(` [verbose] HTTP ${res.status} body: ${res.text.slice(0, 300)}`);
121167
+ }
121168
+ const rawBody = parseCustomerAccessPermissionBody(res.text);
121169
+ return interpretCustomerAccessPermission(mediaCustomerId, res.status, rawBody);
121170
+ }
121171
+ async function runAccountCheckAccess(opts) {
121172
+ const mediaCustomerId = assertGoogleMediaCustomerId(opts.account);
121173
+ const config = loadConfig(opts.token);
121174
+ const payload = await fetchGoogleCustomerAccessPermission(
121175
+ config,
121176
+ mediaCustomerId,
121177
+ opts.verbose
121178
+ );
121179
+ if (await emitCliJsonOrSnapshot(opts, {
121180
+ section: "account-check-access",
121181
+ commandLabel: "account check-access",
121182
+ payload
121183
+ })) {
121184
+ if (!payload.accessible) {
121185
+ process.exit(1);
121186
+ }
121187
+ return;
121188
+ }
121189
+ const statusLabel = {
121190
+ accessible: "\u53EF\u8BBF\u95EE",
121191
+ reauth_required: "\u9700\u91CD\u65B0\u6388\u6743",
121192
+ no_permission: "\u65E0\u6743\u9650",
121193
+ google_not_bound: "\u672A\u7ED1\u5B9A Google",
121194
+ siluzan_token_invalid: "\u4E1D\u8DEF\u8D5E\u51ED\u636E\u5931\u6548",
121195
+ unknown: "\u672A\u77E5"
121196
+ };
121197
+ console.log(`
121198
+ Google \u8D26\u6237\u8BBF\u95EE\u6743\u9650\u6821\u9A8C ${mediaCustomerId}`);
121199
+ console.log(` \u72B6\u6001 : ${statusLabel[payload.status]} (${payload.status})`);
121200
+ console.log(` HTTP : ${payload.httpStatus}`);
121201
+ console.log(` \u53EF\u8BBF\u95EE : ${payload.accessible ? "\u662F" : "\u5426"}`);
121202
+ console.log(` \u8BF4\u660E : ${payload.message}`);
121203
+ if (payload.hint) {
121204
+ console.log(` \u5EFA\u8BAE : ${payload.hint}`);
121205
+ }
121206
+ console.log();
121207
+ if (!payload.accessible) {
121208
+ process.exit(1);
121209
+ }
121210
+ }
121211
+
120854
121212
  // src/commands/account-manage/share.ts
120855
121213
  init_auth();
120856
121214
  init_cli_json_snapshot();
@@ -121075,150 +121433,6 @@ async function runAccountReauth(opts) {
121075
121433
  });
121076
121434
  }
121077
121435
 
121078
- // src/commands/account-manage/google-mcc.ts
121079
- init_auth();
121080
- init_cli_json_snapshot();
121081
- function parseMccManagerIds(input) {
121082
- const normalized = input.replace(/[,,;、]/g, ";");
121083
- return normalized.split(";").map((part) => {
121084
- const digits = part.replace(/\D/g, "");
121085
- return digits ? +digits : NaN;
121086
- }).filter((n) => Number.isFinite(n) && n > 0);
121087
- }
121088
- var MCC_AGENT_TYPES = ["ChengGongYi", "SiGeUS", "SiGeCN", "Dee"];
121089
- function googleGatewayBase(config) {
121090
- const raw = (config.googleApiUrl ?? "").replace(/\/$/, "");
121091
- if (!raw) {
121092
- console.error("\n\u274C Google \u7F51\u5173\u5730\u5740\u672A\u80FD\u63A8\u5BFC\uFF0C\u8BF7\u68C0\u67E5 tsoApiBaseUrl \u914D\u7F6E\u3002\n");
121093
- process.exit(1);
121094
- }
121095
- const err = validateBaseUrl(raw);
121096
- if (err) {
121097
- console.error(`
121098
- \u274C googleApiUrl \u4E0D\u5408\u6CD5\uFF1A${err}
121099
- `);
121100
- process.exit(1);
121101
- }
121102
- return raw;
121103
- }
121104
- function mccResponseHasSuccess(res, managerId) {
121105
- if (!Array.isArray(res)) return false;
121106
- return res.some((item) => String(item) === String(managerId));
121107
- }
121108
- async function runAccountMccBind(opts) {
121109
- const config = loadConfig(opts.token);
121110
- const base = googleGatewayBase(config);
121111
- const managerIds = parseMccManagerIds(opts.mcc);
121112
- if (managerIds.length === 0) {
121113
- console.error("\n\u274C --mcc \u4E2D\u672A\u89E3\u6790\u51FA\u6709\u6548\u7684\u6570\u5B57\u578B MCC \u5BA2\u6237 ID\n");
121114
- process.exit(1);
121115
- }
121116
- if (opts.customers.length === 0) {
121117
- console.error("\n\u274C \u8BF7\u901A\u8FC7 --customers \u6307\u5B9A\u81F3\u5C11\u4E00\u4E2A Google \u5B50\u8D26\u6237 mediaCustomerId\n");
121118
- process.exit(1);
121119
- }
121120
- const body = { agentTypes: [...MCC_AGENT_TYPES], managerIds };
121121
- const results = [];
121122
- for (const customerId of opts.customers) {
121123
- const url = `${base}/command/media-account/${customerId}/CustomerLinks`;
121124
- try {
121125
- const response = await apiFetch2(
121126
- url,
121127
- config,
121128
- { method: "POST", body: JSON.stringify(body) },
121129
- opts.verbose
121130
- );
121131
- results.push({ customerId, response });
121132
- } catch (err) {
121133
- console.error(
121134
- `
121135
- \u274C \u8D26\u6237 ${customerId} MCC \u7ED1\u5B9A\u8BF7\u6C42\u5931\u8D25\uFF1A${err instanceof Error ? err.message : String(err)}
121136
- `
121137
- );
121138
- process.exit(1);
121139
- }
121140
- }
121141
- if (await emitCliJsonOrSnapshot(opts, {
121142
- section: "account-mcc-bind",
121143
- commandLabel: "account mcc-bind",
121144
- payload: results,
121145
- idSuffix: opts.mcc
121146
- })) {
121147
- return;
121148
- }
121149
- let hasFailure = false;
121150
- for (const { customerId, response } of results) {
121151
- const failed = managerIds.filter((mid) => !mccResponseHasSuccess(response, mid));
121152
- if (failed.length > 0) {
121153
- hasFailure = true;
121154
- console.error(` \u274C \u8D26\u6237 ${customerId}\uFF1A\u4EE5\u4E0B MCC \u672A\u5728\u6210\u529F\u5217\u8868\u4E2D \u2192 ${failed.join("\uFF0C")}`);
121155
- } else {
121156
- console.log(
121157
- ` \u2705 \u8D26\u6237 ${customerId}\uFF1A\u5DF2\u5411 ${managerIds.join("\u3001")} \u53D1\u8D77\u7ED1\u5B9A\uFF08\u63A5\u53E3\u8FD4\u56DE\u6210\u529F\uFF09`
121158
- );
121159
- }
121160
- }
121161
- console.log(
121162
- hasFailure ? "\n\u26A0\uFE0F \u90E8\u5206 MCC \u7ED1\u5B9A\u53EF\u80FD\u672A\u751F\u6548\uFF0C\u8BF7\u7ED3\u5408\u7F51\u9875\u6216 --json-out \u6838\u5BF9\u3002\n" : "\n\u2705 MCC \u7ED1\u5B9A\u6D41\u7A0B\u5DF2\u5B8C\u6210\u3002\n"
121163
- );
121164
- }
121165
- async function runAccountMccUnbind(opts) {
121166
- const config = loadConfig(opts.token);
121167
- const base = googleGatewayBase(config);
121168
- const managerIds = parseMccManagerIds(opts.mcc);
121169
- if (managerIds.length === 0) {
121170
- console.error("\n\u274C --mcc \u4E2D\u672A\u89E3\u6790\u51FA\u6709\u6548\u7684\u6570\u5B57\u578B MCC \u5BA2\u6237 ID\n");
121171
- process.exit(1);
121172
- }
121173
- if (opts.customers.length === 0) {
121174
- console.error("\n\u274C \u8BF7\u901A\u8FC7 --customers \u6307\u5B9A\u81F3\u5C11\u4E00\u4E2A Google \u5B50\u8D26\u6237 mediaCustomerId\n");
121175
- process.exit(1);
121176
- }
121177
- const results = [];
121178
- for (const customerId of opts.customers) {
121179
- const url = `${base}/command/media-account/${customerId}/CustomerUnlinks`;
121180
- try {
121181
- const response = await apiFetch2(
121182
- url,
121183
- config,
121184
- { method: "POST", body: JSON.stringify(managerIds) },
121185
- opts.verbose
121186
- );
121187
- results.push({ customerId, response });
121188
- } catch (err) {
121189
- console.error(
121190
- `
121191
- \u274C \u8D26\u6237 ${customerId} MCC \u89E3\u7ED1\u8BF7\u6C42\u5931\u8D25\uFF1A${err instanceof Error ? err.message : String(err)}
121192
- `
121193
- );
121194
- process.exit(1);
121195
- }
121196
- }
121197
- if (await emitCliJsonOrSnapshot(opts, {
121198
- section: "account-mcc-unbind",
121199
- commandLabel: "account mcc-unbind",
121200
- payload: results,
121201
- idSuffix: opts.mcc
121202
- })) {
121203
- return;
121204
- }
121205
- let hasFailure = false;
121206
- for (const { customerId, response } of results) {
121207
- const failed = managerIds.filter((mid) => !mccResponseHasSuccess(response, mid));
121208
- if (failed.length > 0) {
121209
- hasFailure = true;
121210
- console.error(` \u274C \u8D26\u6237 ${customerId}\uFF1A\u4EE5\u4E0B MCC \u672A\u5728\u6210\u529F\u89E3\u7ED1\u5217\u8868\u4E2D \u2192 ${failed.join("\uFF0C")}`);
121211
- } else {
121212
- console.log(
121213
- ` \u2705 \u8D26\u6237 ${customerId}\uFF1A\u5DF2\u5411 ${managerIds.join("\u3001")} \u53D1\u8D77\u89E3\u7ED1\uFF08\u63A5\u53E3\u8FD4\u56DE\u6210\u529F\uFF09`
121214
- );
121215
- }
121216
- }
121217
- console.log(
121218
- hasFailure ? "\n\u26A0\uFE0F \u90E8\u5206 MCC \u89E3\u7ED1\u53EF\u80FD\u672A\u751F\u6548\uFF0C\u8BF7\u7ED3\u5408\u7F51\u9875\u6216 --json-out \u6838\u5BF9\u3002\n" : "\n\u2705 MCC \u89E3\u7ED1\u6D41\u7A0B\u5DF2\u5B8C\u6210\u3002\n"
121219
- );
121220
- }
121221
-
121222
121436
  // src/commands/account-manage/tiktok-close.ts
121223
121437
  init_auth();
121224
121438
  init_cli_json_snapshot();
@@ -121988,6 +122202,21 @@ function register23(program2) {
121988
122202
  });
121989
122203
  }
121990
122204
  );
122205
+ accountCmd.command("check-access").description(
122206
+ "\u6821\u9A8C Google \u5E7F\u544A\u8D26\u6237\u8BBF\u95EE\u6743\u9650\uFF08GET googleapi \u2026/customer-access-permission\uFF09\uFF1B403 \u901A\u5E38\u8868\u793A\u8D26\u6237\u4E0D\u5728\u5F53\u524D\u4E1D\u8DEF\u8D5E\u8D26\u53F7\u4E0B"
122207
+ ).requiredOption(
122208
+ "-a, --account <mediaCustomerId>",
122209
+ "Google mediaCustomerId\uFF08\u7EAF\u6570\u5B57\uFF0C\u4E0E list-accounts -m Google \u4E00\u81F4\uFF09"
122210
+ ).option("-t, --token <token>", "Auth Token").option("--json-out <path>", "\u843D\u76D8 JSON\uFF1Bstdout \u4E00\u884C\u6458\u8981\uFF1B\u4E0D\u53EF\u8BBF\u95EE\u65F6 exit 1").option("--verbose", "\u6253\u5370\u539F\u59CB HTTP \u54CD\u5E94\u7247\u6BB5", false).action(
122211
+ async (opts) => {
122212
+ await runAccountCheckAccess({
122213
+ account: opts.account,
122214
+ token: opts.token,
122215
+ jsonOut: opts.jsonOut,
122216
+ verbose: opts.verbose
122217
+ });
122218
+ }
122219
+ );
121991
122220
  accountCmd.command("delink").description(
121992
122221
  "\u89E3\u9664\u6388\u6743\uFF1A\u65AD\u5F00\u5E7F\u544A\u8D26\u6237\u4E0E\u5F53\u524D\u4E1D\u8DEF\u8D5E\u8D26\u53F7\u7684\u5173\u8054\uFF08\u7F51\u9875 manageAccounts\u300C\u89E3\u9664\u6388\u6743\u300D\uFF1B--id \u6216 --ids\uFF09"
121993
122222
  ).option("--id <entityId>", "\u5355\u4E2A\u8D26\u6237 entityId\uFF08\u6765\u81EA list-accounts \u7684 ma.entityId \u5B57\u6BB5\uFF09").option("--ids <entityIds>", "\u6279\u91CF entityId\uFF0C\u9017\u53F7\u5206\u9694\uFF08\u4E0E --id \u4E8C\u9009\u4E00\uFF0C\u53EF\u540C\u65F6\u4F20\uFF09").option("-t, --token <token>", "Auth Token").option("--verbose", "\u8BE6\u7EC6\u9519\u8BEF\u4FE1\u606F", false).action(async (opts) => {
@@ -125282,12 +125511,12 @@ function sectionRecord(env) {
125282
125511
  }
125283
125512
 
125284
125513
  // src/commands/google-ads-diagnosis/build-report-data.ts
125285
- function num(value) {
125514
+ function num2(value) {
125286
125515
  const n = Number(value);
125287
125516
  return Number.isFinite(n) ? n : 0;
125288
125517
  }
125289
125518
  function rowCost(row) {
125290
- return num(row.cost ?? row.spend);
125519
+ return num2(row.cost ?? row.spend);
125291
125520
  }
125292
125521
  function pct(value) {
125293
125522
  return `${(value * 100).toFixed(2)}%`;
@@ -125318,11 +125547,11 @@ function asPeriodRecord(value) {
125318
125547
  }
125319
125548
  function toKeyMetricRow(title, current, previous) {
125320
125549
  const currentCost = rowCost(current);
125321
- const currentClicks = num(current.clicks);
125322
- const currentCvr = num(current.conversionRate ?? current.cvr);
125550
+ const currentClicks = num2(current.clicks);
125551
+ const currentCvr = num2(current.conversionRate ?? current.cvr);
125323
125552
  const previousCost = previous ? rowCost(previous) : null;
125324
- const previousClicks = previous ? num(previous.clicks) : null;
125325
- const previousCvr = previous ? num(previous.conversionRate ?? previous.cvr) : null;
125553
+ const previousClicks = previous ? num2(previous.clicks) : null;
125554
+ const previousCvr = previous ? num2(previous.conversionRate ?? previous.cvr) : null;
125326
125555
  return {
125327
125556
  title,
125328
125557
  currentCost,
@@ -125352,13 +125581,13 @@ function buildComparisonItems(currentRows, previousRows, titleFn, keyFn) {
125352
125581
  }
125353
125582
  function toTargetingItem(row, labelKey, label) {
125354
125583
  const cost = rowCost(row);
125355
- const clicks = num(row.clicks);
125356
- const impressions = num(row.impressions);
125357
- const conversions = num(row.conversions);
125358
- const ctr = num(row.ctr);
125359
- const cvr = num(row.conversionRate ?? row.cvr);
125360
- const cpc = num(row.averageCpc ?? row.cpc);
125361
- const cpa = conversions > 0 ? cost / conversions : num(row.costPerConversion ?? row.cpa);
125584
+ const clicks = num2(row.clicks);
125585
+ const impressions = num2(row.impressions);
125586
+ const conversions = num2(row.conversions);
125587
+ const ctr = num2(row.ctr);
125588
+ const cvr = num2(row.conversionRate ?? row.cvr);
125589
+ const cpc = num2(row.averageCpc ?? row.cpc);
125590
+ const cpa = conversions > 0 ? cost / conversions : num2(row.costPerConversion ?? row.cpa);
125362
125591
  return {
125363
125592
  [labelKey]: label,
125364
125593
  cost,
@@ -125373,11 +125602,11 @@ function toTargetingItem(row, labelKey, label) {
125373
125602
  }
125374
125603
  function toDailyConversionItem(row) {
125375
125604
  const cost = rowCost(row);
125376
- const conversions = num(row.conversions);
125605
+ const conversions = num2(row.conversions);
125377
125606
  const rawDate = String(row.date ?? "");
125378
125607
  const date = rawDate.includes("T") ? rawDate.slice(0, 10) : rawDate.slice(0, 10);
125379
- const cpa = conversions > 0 ? cost / conversions : num(row.costPerConversion ?? row.cpa);
125380
- return { date, cost, conversions, cpa, clicks: num(row.clicks), impressions: num(row.impressions) };
125608
+ const cpa = conversions > 0 ? cost / conversions : num2(row.costPerConversion ?? row.cpa);
125609
+ return { date, cost, conversions, cpa, clicks: num2(row.clicks), impressions: num2(row.impressions) };
125381
125610
  }
125382
125611
  function loadCompanyNameFromSnap(snapDir) {
125383
125612
  const file = path21.join(snapDir, "list-accounts-google.json");
@@ -125414,8 +125643,8 @@ function inferBusinessProfile(campaignRows, adRows, website) {
125414
125643
  };
125415
125644
  }
125416
125645
  function primaryConversionLabel(rows) {
125417
- const sorted = [...rows].sort((a, b) => num(b.allConversions) - num(a.allConversions));
125418
- const top = sorted.find((r) => num(r.allConversions) > 0 && String(r.status) === "Enabled");
125646
+ const sorted = [...rows].sort((a, b) => num2(b.allConversions) - num2(a.allConversions));
125647
+ const top = sorted.find((r) => num2(r.allConversions) > 0 && String(r.status) === "Enabled");
125419
125648
  if (top) return String(top.name ?? "\u8BE2\u76D8/\u8F6C\u5316");
125420
125649
  const enabled = rows.find((r) => String(r.status) === "Enabled");
125421
125650
  return enabled ? String(enabled.name ?? "\u8BE2\u76D8/\u8F6C\u5316") : "\u8BE2\u76D8/\u8F6C\u5316";
@@ -125434,7 +125663,7 @@ function buildBiddingStrategyItems(campaignRows, endDate, gold) {
125434
125663
  const strategy = String(row.biddingStrategyTypeV2 ?? row.biddingStrategyType ?? "UNKNOWN");
125435
125664
  const status = String(row.campaignStatus ?? row.campaignStatusV2 ?? "");
125436
125665
  const isPaused = /paused/i.test(status);
125437
- const recommended = num(row.conversions) > 0 ? "MAXIMIZE_CONVERSIONS" : "TARGET_SPEND";
125666
+ const recommended = num2(row.conversions) > 0 ? "MAXIMIZE_CONVERSIONS" : "TARGET_SPEND";
125438
125667
  const isCorrect = policyOk && !isPaused && strategy === recommended;
125439
125668
  return {
125440
125669
  campaignName: String(row.campaignName ?? "\u672A\u547D\u540D\u7CFB\u5217"),
@@ -125447,16 +125676,16 @@ function buildBiddingStrategyItems(campaignRows, endDate, gold) {
125447
125676
  });
125448
125677
  }
125449
125678
  function buildStructureCounts(resourceRecord, campaignRows, keywordRows, searchRows) {
125450
- const campaignCount = Math.max(num(resourceRecord?.campaignCount), campaignRows.length);
125451
- const keywordCount = Math.max(num(resourceRecord?.keywordCount), keywordRows.length);
125679
+ const campaignCount = Math.max(num2(resourceRecord?.campaignCount), campaignRows.length);
125680
+ const keywordCount = Math.max(num2(resourceRecord?.keywordCount), keywordRows.length);
125452
125681
  return {
125453
125682
  campaignCount,
125454
- adGroupCount: num(resourceRecord?.adGroupCount),
125683
+ adGroupCount: num2(resourceRecord?.adGroupCount),
125455
125684
  keywordCount,
125456
- adCount: num(resourceRecord?.adCount),
125457
- extensionCount: num(resourceRecord?.extensionCount),
125458
- negativeKeywordCount: num(resourceRecord?.negativeKeywordCount),
125459
- countriesWithConversionsCount: num(resourceRecord?.countriesWithConversionsCount),
125685
+ adCount: num2(resourceRecord?.adCount),
125686
+ extensionCount: num2(resourceRecord?.extensionCount),
125687
+ negativeKeywordCount: num2(resourceRecord?.negativeKeywordCount),
125688
+ countriesWithConversionsCount: num2(resourceRecord?.countriesWithConversionsCount),
125460
125689
  searchTermCount: searchRows.length
125461
125690
  };
125462
125691
  }
@@ -125470,26 +125699,26 @@ function loadWebsiteFromAds(snapDir, accountId, start, end) {
125470
125699
  }
125471
125700
  function buildKeywordShareItems(rows) {
125472
125701
  const totalCost = rows.reduce((sum, row) => sum + rowCost(row), 0);
125473
- const totalConversions = rows.reduce((sum, row) => sum + num(row.conversions), 0);
125702
+ const totalConversions = rows.reduce((sum, row) => sum + num2(row.conversions), 0);
125474
125703
  return rows.map((row) => {
125475
125704
  const cost = rowCost(row);
125476
- const conversions = num(row.conversions);
125705
+ const conversions = num2(row.conversions);
125477
125706
  const keywordText = String(row.keywordText ?? row.keyword ?? "").trim();
125478
125707
  return {
125479
125708
  keyword: keywordText || "\u672A\u547D\u540D",
125480
- cpc: num(row.averageCpc ?? row.cpc),
125709
+ cpc: num2(row.averageCpc ?? row.cpc),
125481
125710
  matchType: String(
125482
125711
  row.keywordMatchTypeZh ?? row.keywordMatchType ?? row.matchType ?? ""
125483
125712
  ),
125484
- impressions: num(row.impressions),
125485
- clicks: num(row.clicks),
125713
+ impressions: num2(row.impressions),
125714
+ clicks: num2(row.clicks),
125486
125715
  cost,
125487
125716
  conversions,
125488
125717
  costShare: totalCost > 0 ? cost / totalCost * 100 : 0,
125489
125718
  conversionsShare: totalConversions > 0 ? conversions / totalConversions * 100 : 0,
125490
125719
  cpa: conversions > 0 ? cost / conversions : 0,
125491
- ctr: num(row.ctr),
125492
- cvr: num(row.conversionRate ?? row.cvr)
125720
+ ctr: num2(row.ctr),
125721
+ cvr: num2(row.conversionRate ?? row.cvr)
125493
125722
  };
125494
125723
  });
125495
125724
  }
@@ -125497,17 +125726,17 @@ function buildSearchTermItems(rows) {
125497
125726
  const sorted = [...rows].sort((a, b) => rowCost(b) - rowCost(a));
125498
125727
  return sorted.slice(0, 50).map((row) => {
125499
125728
  const cost = rowCost(row);
125500
- const conversions = num(row.conversions);
125501
- const cpa = conversions > 0 ? num(row.costPerConversion) || cost / conversions : void 0;
125729
+ const conversions = num2(row.conversions);
125730
+ const cpa = conversions > 0 ? num2(row.costPerConversion) || cost / conversions : void 0;
125502
125731
  return {
125503
125732
  searchTerm: String(row.searchTermText ?? row.searchTerm ?? "\u672A\u547D\u540D"),
125504
125733
  keyword: String(row.keywordText ?? row.keyword ?? ""),
125505
125734
  cost,
125506
125735
  conversions,
125507
- clicks: num(row.clicks),
125508
- impressions: num(row.impressions),
125509
- ctr: num(row.ctr),
125510
- cvr: num(row.conversionRate ?? row.cvr),
125736
+ clicks: num2(row.clicks),
125737
+ impressions: num2(row.impressions),
125738
+ ctr: num2(row.ctr),
125739
+ cvr: num2(row.conversionRate ?? row.cvr),
125511
125740
  cpa
125512
125741
  };
125513
125742
  });
@@ -125549,18 +125778,18 @@ function buildAdCreativeItems(rows) {
125549
125778
  }
125550
125779
  function buildBudgetCompetitiveness(overviewRecord, dimensionRecord) {
125551
125780
  const current = asPeriodRecord(overviewRecord?.currentPeriod);
125552
- const totalCost = num(overviewRecord?.totalCost ?? current?.spend);
125553
- const activeDays = num(overviewRecord?.activeDays);
125554
- const avgDaily = num(overviewRecord?.averageDailyCost ?? (activeDays > 0 ? totalCost / activeDays : 0));
125555
- const remainingBudget = num(overviewRecord?.remainingAccountBudget);
125781
+ const totalCost = num2(overviewRecord?.totalCost ?? current?.spend);
125782
+ const activeDays = num2(overviewRecord?.activeDays);
125783
+ const avgDaily = num2(overviewRecord?.averageDailyCost ?? (activeDays > 0 ? totalCost / activeDays : 0));
125784
+ const remainingBudget = num2(overviewRecord?.remainingAccountBudget);
125556
125785
  const utilization = remainingBudget > 0 && avgDaily > 0 ? Math.min(100, avgDaily / (avgDaily + remainingBudget / Math.max(activeDays, 1)) * 100) : avgDaily > 0 ? 85 : 0;
125557
- const sis = num(
125786
+ const sis = num2(
125558
125787
  dimensionRecord?.searchImpressionShare ?? current?.searchImpressionShare
125559
125788
  );
125560
- const budgetLost = num(
125789
+ const budgetLost = num2(
125561
125790
  dimensionRecord?.searchBudgetLostImpressionShare ?? current?.searchBudgetLostImpressionShare
125562
125791
  );
125563
- const rankLost = num(
125792
+ const rankLost = num2(
125564
125793
  dimensionRecord?.searchRankLostImpressionShare ?? current?.searchRankLostImpressionShare
125565
125794
  );
125566
125795
  return [
@@ -125646,17 +125875,17 @@ function buildGoogleAdsDiagnosisCollectPayload(opts) {
125646
125875
  };
125647
125876
  data.reportDate = end;
125648
125877
  const cost = rowCost(currentPeriod ?? overviewRecord ?? {});
125649
- const impressions = num(currentPeriod?.impressions);
125650
- const clicks = num(currentPeriod?.clicks);
125651
- const conversions = num(currentPeriod?.conversions);
125652
- const ctr = num(currentPeriod?.ctr);
125653
- const cvr = num(currentPeriod?.conversionRate ?? currentPeriod?.cvr);
125654
- const cpa = conversions > 0 ? cost / conversions : num(currentPeriod?.costPerConversion);
125878
+ const impressions = num2(currentPeriod?.impressions);
125879
+ const clicks = num2(currentPeriod?.clicks);
125880
+ const conversions = num2(currentPeriod?.conversions);
125881
+ const ctr = num2(currentPeriod?.ctr);
125882
+ const cvr = num2(currentPeriod?.conversionRate ?? currentPeriod?.cvr);
125883
+ const cpa = conversions > 0 ? cost / conversions : num2(currentPeriod?.costPerConversion);
125655
125884
  const prevCost = previousPeriod ? rowCost(previousPeriod) : 0;
125656
- const prevConversions = previousPeriod ? num(previousPeriod.conversions) : 0;
125657
- const prevCtr = previousPeriod ? num(previousPeriod.ctr) : 0;
125658
- const prevCvr = previousPeriod ? num(previousPeriod.conversionRate ?? previousPeriod.cvr) : 0;
125659
- const prevCpa = prevConversions > 0 ? rowCost(previousPeriod) / prevConversions : num(previousPeriod?.costPerConversion);
125885
+ const prevConversions = previousPeriod ? num2(previousPeriod.conversions) : 0;
125886
+ const prevCtr = previousPeriod ? num2(previousPeriod.ctr) : 0;
125887
+ const prevCvr = previousPeriod ? num2(previousPeriod.conversionRate ?? previousPeriod.cvr) : 0;
125888
+ const prevCpa = prevConversions > 0 ? rowCost(previousPeriod) / prevConversions : num2(previousPeriod?.costPerConversion);
125660
125889
  data.metrics = {
125661
125890
  cost,
125662
125891
  impressions,
@@ -125692,8 +125921,8 @@ function buildGoogleAdsDiagnosisCollectPayload(opts) {
125692
125921
  items: conversionRows.map((row) => ({
125693
125922
  name: String(row.name ?? row.eventName ?? "\u672A\u77E5\u4E8B\u4EF6"),
125694
125923
  status: String(row.status ?? ""),
125695
- allConversions: num(row.allConversions),
125696
- allConversionsValue: num(row.allConversionsValue)
125924
+ allConversions: num2(row.allConversions),
125925
+ allConversionsValue: num2(row.allConversionsValue)
125697
125926
  }))
125698
125927
  };
125699
125928
  const campaignPrevious = sectionItems(
@@ -125777,16 +126006,16 @@ function buildGoogleAdsDiagnosisCollectPayload(opts) {
125777
126006
  const lighthouse = opts.landingPage ?? {};
125778
126007
  data.landingPageAnalysis = {
125779
126008
  desktop: {
125780
- score: num(lighthouse.desktop?.score),
125781
- firstContentfulPaint: num(lighthouse.desktop?.firstContentfulPaint),
125782
- firstMeaningfulPaint: num(lighthouse.desktop?.firstMeaningfulPaint),
125783
- speedIndex: num(lighthouse.desktop?.speedIndex)
126009
+ score: num2(lighthouse.desktop?.score),
126010
+ firstContentfulPaint: num2(lighthouse.desktop?.firstContentfulPaint),
126011
+ firstMeaningfulPaint: num2(lighthouse.desktop?.firstMeaningfulPaint),
126012
+ speedIndex: num2(lighthouse.desktop?.speedIndex)
125784
126013
  },
125785
126014
  mobile: {
125786
- score: num(lighthouse.mobile?.score),
125787
- firstContentfulPaint: num(lighthouse.mobile?.firstContentfulPaint),
125788
- firstMeaningfulPaint: num(lighthouse.mobile?.firstMeaningfulPaint),
125789
- speedIndex: num(lighthouse.mobile?.speedIndex)
126015
+ score: num2(lighthouse.mobile?.score),
126016
+ firstContentfulPaint: num2(lighthouse.mobile?.firstContentfulPaint),
126017
+ firstMeaningfulPaint: num2(lighthouse.mobile?.firstMeaningfulPaint),
126018
+ speedIndex: num2(lighthouse.mobile?.speedIndex)
125790
126019
  }
125791
126020
  };
125792
126021
  const keywordShareItems = buildKeywordShareItems(kwCurrent);
@@ -125820,7 +126049,7 @@ function buildGoogleAdsDiagnosisCollectPayload(opts) {
125820
126049
  data.newFeatures = {
125821
126050
  items: nfItems.map((item) => {
125822
126051
  const strategy = String(item.strategy ?? "");
125823
- const enabled = strategy === "PerformanceMax" ? num(campaignTypes?.PerformanceMax) > 0 : strategy === "KwMatch" ? num(campaignTypes?.Search) > 0 : strategy === "DemandGen" ? num(campaignTypes?.DemandGen) > 0 : strategy === "Youtube" ? num(campaignTypes?.Video) > 0 || num(campaignTypes?.Youtube) > 0 : false;
126052
+ const enabled = strategy === "PerformanceMax" ? num2(campaignTypes?.PerformanceMax) > 0 : strategy === "KwMatch" ? num2(campaignTypes?.Search) > 0 : strategy === "DemandGen" ? num2(campaignTypes?.DemandGen) > 0 : strategy === "Youtube" ? num2(campaignTypes?.Video) > 0 || num2(campaignTypes?.Youtube) > 0 : false;
125824
126053
  return {
125825
126054
  ...item,
125826
126055
  accountStatus: enabled,
@@ -125830,7 +126059,7 @@ function buildGoogleAdsDiagnosisCollectPayload(opts) {
125830
126059
  ...emptyModuleNarrative()
125831
126060
  };
125832
126061
  data.summary = emptySummary();
125833
- const zeroConvKw = kwCurrent.filter((r) => rowCost(r) > 50 && num(r.conversions) === 0);
126062
+ const zeroConvKw = kwCurrent.filter((r) => rowCost(r) > 50 && num2(r.conversions) === 0);
125834
126063
  const agentBrief = buildGoogleAdsDiagnosisAgentBrief({
125835
126064
  accountId,
125836
126065
  period: formatPeriod(start, end),
@@ -125854,9 +126083,9 @@ function buildGoogleAdsDiagnosisCollectPayload(opts) {
125854
126083
  prevCpa
125855
126084
  },
125856
126085
  dimension: {
125857
- searchImpressionShare: num(dimensionRecord?.searchImpressionShare),
125858
- searchBudgetLostImpressionShare: num(dimensionRecord?.searchBudgetLostImpressionShare),
125859
- searchRankLostImpressionShare: num(dimensionRecord?.searchRankLostImpressionShare)
126086
+ searchImpressionShare: num2(dimensionRecord?.searchImpressionShare),
126087
+ searchBudgetLostImpressionShare: num2(dimensionRecord?.searchBudgetLostImpressionShare),
126088
+ searchRankLostImpressionShare: num2(dimensionRecord?.searchRankLostImpressionShare)
125860
126089
  },
125861
126090
  structure: structureCounts,
125862
126091
  goldAccount: goldRecord,
@@ -126502,7 +126731,7 @@ var PHASE_LABEL = {
126502
126731
  "find-winner": "\u627E\u8D62\u5BB6",
126503
126732
  scale: "\u653E\u91CF"
126504
126733
  };
126505
- function num2(value) {
126734
+ function num3(value) {
126506
126735
  const n = Number(value);
126507
126736
  return Number.isFinite(n) ? n : void 0;
126508
126737
  }
@@ -126513,35 +126742,35 @@ function money(value, currency = "USD") {
126513
126742
  }
126514
126743
  function adSetsWithSpend(tables) {
126515
126744
  const rows = Array.isArray(tables?.adSets) ? tables.adSets : [];
126516
- return rows.filter((r) => (num2(r.spend) ?? 0) > 0);
126745
+ return rows.filter((r) => (num3(r.spend) ?? 0) > 0);
126517
126746
  }
126518
126747
  function topAdSetConcentration(adSets) {
126519
126748
  if (!adSets.length) return null;
126520
- const totalSpend = adSets.reduce((s, r) => s + (num2(r.spend) ?? 0), 0);
126521
- const totalResults = adSets.reduce((s, r) => s + (num2(r.results) ?? 0), 0);
126749
+ const totalSpend = adSets.reduce((s, r) => s + (num3(r.spend) ?? 0), 0);
126750
+ const totalResults = adSets.reduce((s, r) => s + (num3(r.results) ?? 0), 0);
126522
126751
  let best = adSets[0];
126523
- let bestSpend = num2(best.spend) ?? 0;
126752
+ let bestSpend = num3(best.spend) ?? 0;
126524
126753
  for (const r of adSets) {
126525
- const sp = num2(r.spend) ?? 0;
126754
+ const sp = num3(r.spend) ?? 0;
126526
126755
  if (sp > bestSpend) {
126527
126756
  best = r;
126528
126757
  bestSpend = sp;
126529
126758
  }
126530
126759
  }
126531
- const results = num2(best.results) ?? 0;
126760
+ const results = num3(best.results) ?? 0;
126532
126761
  return {
126533
126762
  name: String(best.adGroupName ?? best.name ?? "\u2014"),
126534
126763
  spendShare: totalSpend > 0 ? bestSpend / totalSpend : 0,
126535
126764
  resultsShare: totalResults > 0 ? results / totalResults : 0,
126536
- cpl: num2(best.costPerResult)
126765
+ cpl: num3(best.costPerResult)
126537
126766
  };
126538
126767
  }
126539
126768
  function winnerSpendShare(adSets, avgCpl, totalSpend) {
126540
126769
  if (totalSpend <= 0 || avgCpl <= 0) return 0;
126541
126770
  const winnerSpend = adSets.filter((r) => {
126542
- const cpl = num2(r.costPerResult);
126771
+ const cpl = num3(r.costPerResult);
126543
126772
  return cpl != null && cpl <= avgCpl * 0.9;
126544
- }).reduce((s, r) => s + (num2(r.spend) ?? 0), 0);
126773
+ }).reduce((s, r) => s + (num3(r.spend) ?? 0), 0);
126545
126774
  return winnerSpend / totalSpend;
126546
126775
  }
126547
126776
  function countActiveDimensions(charts, tables) {
@@ -126558,7 +126787,7 @@ function asArray(value) {
126558
126787
  }
126559
126788
  function inferLifecyclePhase(input) {
126560
126789
  const { avgCpl, currency = "USD" } = input;
126561
- const spend = num2(input.spend) ?? 0;
126790
+ const spend = num3(input.spend) ?? 0;
126562
126791
  const adSets = adSetsWithSpend(input.tables);
126563
126792
  const activeDims = countActiveDimensions(input.charts, input.tables);
126564
126793
  const winShare = winnerSpendShare(adSets, avgCpl, spend);
@@ -126617,7 +126846,7 @@ function ensureLifecycleFromSnapshot(healthDiagnosis, merged) {
126617
126846
  }
126618
126847
 
126619
126848
  // src/commands/facebook-analysis/scorecard-autofill.ts
126620
- function num3(value) {
126849
+ function num4(value) {
126621
126850
  const n = Number(value);
126622
126851
  return Number.isFinite(n) ? n : void 0;
126623
126852
  }
@@ -126657,8 +126886,8 @@ function buildScorecardFromMergedData(input) {
126657
126886
  if (pLabels.length) {
126658
126887
  const indexed = pLabels.map((label, i) => ({
126659
126888
  label: String(label),
126660
- cpl: num3(pCpl[i]),
126661
- spend: num3(pSpend[i]) ?? 0
126889
+ cpl: num4(pCpl[i]),
126890
+ spend: num4(pSpend[i]) ?? 0
126662
126891
  })).filter((x) => x.spend > 0 || x.cpl != null && x.cpl > 0);
126663
126892
  const sorted = [...indexed].sort((a, b) => (a.cpl ?? Infinity) - (b.cpl ?? Infinity));
126664
126893
  if (sorted[0]) {
@@ -126683,7 +126912,7 @@ function buildScorecardFromMergedData(input) {
126683
126912
  const cLabels = Array.isArray(country?.labels) ? country.labels : [];
126684
126913
  const cCpl = Array.isArray(country?.cpl) ? country.cpl : [];
126685
126914
  if (cLabels.length) {
126686
- const indexed = cLabels.map((label, i) => ({ label: String(label), cpl: num3(cCpl[i]) })).filter((x) => x.cpl != null && x.cpl > 0);
126915
+ const indexed = cLabels.map((label, i) => ({ label: String(label), cpl: num4(cCpl[i]) })).filter((x) => x.cpl != null && x.cpl > 0);
126687
126916
  const sorted = [...indexed].sort((a, b) => (a.cpl ?? 0) - (b.cpl ?? 0));
126688
126917
  if (sorted[0]) {
126689
126918
  const s = cplSignal(sorted[0].cpl, avgCpl);
@@ -126704,24 +126933,24 @@ function buildScorecardFromMergedData(input) {
126704
126933
  }
126705
126934
  }
126706
126935
  const adSets = Array.isArray(tables.adSets) ? tables.adSets : [];
126707
- const adWithSpend = adSets.filter((g) => (num3(g.spend) ?? 0) > 0);
126936
+ const adWithSpend = adSets.filter((g) => (num4(g.spend) ?? 0) > 0);
126708
126937
  if (adWithSpend.length) {
126709
126938
  const sorted = [...adWithSpend].sort(
126710
- (a, b) => (num3(a.costPerResult) ?? Infinity) - (num3(b.costPerResult) ?? Infinity)
126939
+ (a, b) => (num4(a.costPerResult) ?? Infinity) - (num4(b.costPerResult) ?? Infinity)
126711
126940
  );
126712
126941
  const best = sorted[0];
126713
- const sBest = cplSignal(num3(best.costPerResult), avgCpl);
126942
+ const sBest = cplSignal(num4(best.costPerResult), avgCpl);
126714
126943
  pushRow(rows, seen, {
126715
126944
  item: String(best.name ?? "\u5E7F\u544A\u7EC4"),
126716
- data: `CPL ${money2(num3(best.costPerResult), currency)}`,
126945
+ data: `CPL ${money2(num4(best.costPerResult), currency)}`,
126717
126946
  ...sBest
126718
126947
  });
126719
126948
  if (sorted.length > 1) {
126720
126949
  const worst = sorted[sorted.length - 1];
126721
- const sWorst = cplSignal(num3(worst.costPerResult), avgCpl);
126950
+ const sWorst = cplSignal(num4(worst.costPerResult), avgCpl);
126722
126951
  pushRow(rows, seen, {
126723
126952
  item: String(worst.name ?? "\u5E7F\u544A\u7EC4"),
126724
- data: `CPL ${money2(num3(worst.costPerResult), currency)}`,
126953
+ data: `CPL ${money2(num4(worst.costPerResult), currency)}`,
126725
126954
  ...sWorst
126726
126955
  });
126727
126956
  }
@@ -126729,18 +126958,18 @@ function buildScorecardFromMergedData(input) {
126729
126958
  const topAud = Array.isArray(tables.audienceTop) ? tables.audienceTop : [];
126730
126959
  const bottomAud = Array.isArray(tables.audienceBottom) ? tables.audienceBottom : [];
126731
126960
  if (topAud[0]) {
126732
- const s = cplSignal(num3(topAud[0].costPerResult), avgCpl);
126961
+ const s = cplSignal(num4(topAud[0].costPerResult), avgCpl);
126733
126962
  pushRow(rows, seen, {
126734
126963
  item: `\u53D7\u4F17\xB7${String(topAud[0].label ?? "Top")}`,
126735
- data: `CPL ${money2(num3(topAud[0].costPerResult), currency)}`,
126964
+ data: `CPL ${money2(num4(topAud[0].costPerResult), currency)}`,
126736
126965
  ...s
126737
126966
  });
126738
126967
  }
126739
126968
  if (bottomAud[0]) {
126740
- const s = cplSignal(num3(bottomAud[0].costPerResult), avgCpl);
126969
+ const s = cplSignal(num4(bottomAud[0].costPerResult), avgCpl);
126741
126970
  pushRow(rows, seen, {
126742
126971
  item: `\u53D7\u4F17\xB7${String(bottomAud[0].label ?? "Bottom")}`,
126743
- data: `CPL ${money2(num3(bottomAud[0].costPerResult), currency)}`,
126972
+ data: `CPL ${money2(num4(bottomAud[0].costPerResult), currency)}`,
126744
126973
  ...s
126745
126974
  });
126746
126975
  }
@@ -126784,7 +127013,7 @@ function asRecord8(value) {
126784
127013
  }
126785
127014
  return null;
126786
127015
  }
126787
- function num4(value) {
127016
+ function num5(value) {
126788
127017
  const n = Number(value);
126789
127018
  return Number.isFinite(n) ? n : void 0;
126790
127019
  }
@@ -126838,8 +127067,8 @@ function aggregatePlatform(networks) {
126838
127067
  const platform = String(row.publisherPlatform ?? row.network ?? "unknown");
126839
127068
  const prev = map.get(platform) ?? { spend: 0, results: 0 };
126840
127069
  map.set(platform, {
126841
- spend: prev.spend + (num4(row.spend) ?? 0),
126842
- results: prev.results + (num4(row.results) ?? 0)
127070
+ spend: prev.spend + (num5(row.spend) ?? 0),
127071
+ results: prev.results + (num5(row.results) ?? 0)
126843
127072
  });
126844
127073
  }
126845
127074
  const labels = [];
@@ -126855,10 +127084,10 @@ function aggregatePlatform(networks) {
126855
127084
  function buildAudienceRows(audiences) {
126856
127085
  return audiences.map((a) => ({
126857
127086
  label: audienceLabel(a.age, a.gender),
126858
- spend: num4(a.spend) ?? 0,
126859
- results: num4(a.results) ?? 0,
126860
- costPerResult: num4(a.costPerResult) ?? (num4(a.results) ? (num4(a.spend) ?? 0) / (num4(a.results) ?? 1) : 0),
126861
- frequency: num4(a.frequency)
127087
+ spend: num5(a.spend) ?? 0,
127088
+ results: num5(a.results) ?? 0,
127089
+ costPerResult: num5(a.costPerResult) ?? (num5(a.results) ? (num5(a.spend) ?? 0) / (num5(a.results) ?? 1) : 0),
127090
+ frequency: num5(a.frequency)
126862
127091
  })).filter((r) => r.spend > 0 || r.results > 0);
126863
127092
  }
126864
127093
  async function mergeFacebookSnapshotIntoReport(payload, snapshotDir) {
@@ -126897,7 +127126,7 @@ async function mergeFacebookSnapshotIntoReport(payload, snapshotDir) {
126897
127126
  }
126898
127127
  if (!meta.resultType && current?.resultType) meta.resultType = String(current.resultType);
126899
127128
  const charts = { ...payload.charts ?? {} };
126900
- const avgCpl = num4(kpis.costPerResult) ?? 0;
127129
+ const avgCpl = num5(kpis.costPerResult) ?? 0;
126901
127130
  if (!charts.platform) {
126902
127131
  const platform = sectionMap.get("platform");
126903
127132
  const networks = Array.isArray(platform?.networks) ? platform.networks : [];
@@ -126907,10 +127136,10 @@ async function mergeFacebookSnapshotIntoReport(payload, snapshotDir) {
126907
127136
  const country = sectionMap.get("country");
126908
127137
  const countries = Array.isArray(country?.countries) ? country.countries : [];
126909
127138
  if (countries.length) {
126910
- const sorted = [...countries].sort((a, b) => (num4(b.spend) ?? 0) - (num4(a.spend) ?? 0));
127139
+ const sorted = [...countries].sort((a, b) => (num5(b.spend) ?? 0) - (num5(a.spend) ?? 0));
126911
127140
  charts.country = {
126912
127141
  labels: sorted.map((c) => String(c.countryOrRegion ?? "\u2014")),
126913
- cpl: sorted.map((c) => num4(c.costPerResult) ?? 0)
127142
+ cpl: sorted.map((c) => num5(c.costPerResult) ?? 0)
126914
127143
  };
126915
127144
  }
126916
127145
  }
@@ -126926,7 +127155,7 @@ async function mergeFacebookSnapshotIntoReport(payload, snapshotDir) {
126926
127155
  };
126927
127156
  }
126928
127157
  }
126929
- if (!charts.funnel && num4(kpis.reach) != null) {
127158
+ if (!charts.funnel && num5(kpis.reach) != null) {
126930
127159
  charts.funnel = { reach: kpis.reach, results: kpis.results ?? 0 };
126931
127160
  }
126932
127161
  const tables = { ...payload.tables ?? {} };
@@ -126935,14 +127164,14 @@ async function mergeFacebookSnapshotIntoReport(payload, snapshotDir) {
126935
127164
  const groups = Array.isArray(adSets?.adGroups) ? adSets.adGroups : [];
126936
127165
  if (groups.length) {
126937
127166
  tables.adSets = groups.map((g) => {
126938
- const cpl = num4(g.costPerResult);
126939
- const freq = num4(g.frequency);
127167
+ const cpl = num5(g.costPerResult);
127168
+ const freq = num5(g.frequency);
126940
127169
  const fatigue = fatigueFromFrequency(freq);
126941
127170
  const status = statusFromCpl(cpl, avgCpl);
126942
127171
  return {
126943
127172
  name: String(g.adGroupName ?? g.campaignName ?? "\u2014"),
126944
- spend: num4(g.spend) ?? 0,
126945
- results: num4(g.results) ?? 0,
127173
+ spend: num5(g.spend) ?? 0,
127174
+ results: num5(g.results) ?? 0,
126946
127175
  costPerResult: cpl ?? 0,
126947
127176
  frequency: freq,
126948
127177
  fatigueLevel: fatigue.level,
@@ -126966,7 +127195,7 @@ async function mergeFacebookSnapshotIntoReport(payload, snapshotDir) {
126966
127195
  }
126967
127196
  const currency = typeof meta.currency === "string" ? meta.currency : "USD";
126968
127197
  const mergeCtx = {
126969
- spend: num4(kpis.spend),
127198
+ spend: num5(kpis.spend),
126970
127199
  avgCpl,
126971
127200
  currency,
126972
127201
  charts,