siluzan-tso-cli 1.1.49-beta.3 → 1.1.49-beta.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -51,7 +51,7 @@ siluzan-tso init -d /path/to/skills # 写入自定义目录
51
51
  siluzan-tso init --force # 强制覆盖已存在文件
52
52
  ```
53
53
 
54
- > **注意**:当前为测试版(1.1.49-beta.3),供内部测试使用。正式发布后安装命令将改为 `npm install -g siluzan-tso-cli`。
54
+ > **注意**:当前为测试版(1.1.49-beta.4),供内部测试使用。正式发布后安装命令将改为 `npm install -g siluzan-tso-cli`。
55
55
 
56
56
  | 助手 | 建议 `--ai` |
57
57
  | ----------------------- | ------------------------------------ |
package/dist/index.js CHANGED
@@ -117511,6 +117511,35 @@ function liveAdHasHeadline(ad, headline) {
117511
117511
  if (!h) return false;
117512
117512
  return collectRsaHeadlines(ad).includes(h);
117513
117513
  }
117514
+ function overlapHeadlineCount(planned, live) {
117515
+ const liveSet = new Set(collectRsaHeadlines(live));
117516
+ let n = 0;
117517
+ for (const h of planned) {
117518
+ const key = h.trim().toLowerCase();
117519
+ if (key && liveSet.has(key)) n += 1;
117520
+ }
117521
+ return n;
117522
+ }
117523
+ function pickLiveAdForPlannedRsa(plannedHeadlines, primary, unmatchedLiveAds) {
117524
+ if (unmatchedLiveAds.length === 0) return null;
117525
+ const primaryKey = primary.trim().toLowerCase();
117526
+ let bestIdx = -1;
117527
+ let bestScore = 0;
117528
+ for (let i = 0; i < unmatchedLiveAds.length; i++) {
117529
+ const live = unmatchedLiveAds[i];
117530
+ const overlap = overlapHeadlineCount(plannedHeadlines, live);
117531
+ if (overlap <= 0) continue;
117532
+ const hasPrimary = primaryKey.length > 0 && liveAdHasHeadline(live, primary);
117533
+ const score = (hasPrimary ? 1e3 : 0) + overlap;
117534
+ if (score > bestScore) {
117535
+ bestScore = score;
117536
+ bestIdx = i;
117537
+ }
117538
+ }
117539
+ if (bestIdx < 0) return null;
117540
+ const [picked] = unmatchedLiveAds.splice(bestIdx, 1);
117541
+ return picked ?? null;
117542
+ }
117514
117543
  function livePoolHasText(pool, text2) {
117515
117544
  const t = text2.trim().toLowerCase();
117516
117545
  return t.length > 0 && pool.has(t);
@@ -117768,12 +117797,7 @@ function compareCampaignCreateToLive(cfg, campaignId, live, meta) {
117768
117797
  const liveAdsInGroup = live.ads.filter(
117769
117798
  (a) => belongsToCampaign(a, campaignId, campaignName) && belongsToAdGroup(a, groupName, liveGroupId)
117770
117799
  );
117771
- const liveHeadlinePool = /* @__PURE__ */ new Set();
117772
- const liveDescPool = /* @__PURE__ */ new Set();
117773
- for (const la of liveAdsInGroup) {
117774
- for (const h of collectRsaHeadlines(la)) liveHeadlinePool.add(h);
117775
- for (const d of collectRsaDescriptions(la)) liveDescPool.add(d);
117776
- }
117800
+ const unmatchedLiveAds = [...liveAdsInGroup];
117777
117801
  const ads = g["AdsForBatchJob"];
117778
117802
  if (Array.isArray(ads)) {
117779
117803
  for (let ai = 0; ai < ads.length; ai++) {
@@ -117783,11 +117807,14 @@ function compareCampaignCreateToLive(cfg, campaignId, live, meta) {
117783
117807
  const primary = pickString2(ad["headlinePart1"], ad["AdTitle"]);
117784
117808
  const finalUrl = pickString2(ad["Finalurl"], ad["DestinationUrl"], ad["finalUrl"]);
117785
117809
  const rsaAssets = listPlannedRsaAssets(ad, groupPath, ai, groupName);
117810
+ const plannedHeadlines = rsaAssets.filter((a) => a.kind === "headline").map((a) => a.text);
117811
+ const adLabel = primary || `AdsForBatchJob[${ai}]`;
117786
117812
  if (groupAbsent) {
117787
117813
  pushItem(items3, {
117788
117814
  layer: "ad",
117789
117815
  path: path50,
117790
117816
  adGroupName: groupName,
117817
+ adLabel,
117791
117818
  plannedContent: primary ? `RSA \u9996\u6807\u9898: ${primary}${finalUrl ? ` | \u843D\u5730\u9875: ${finalUrl}` : ""}` : `AdsForBatchJob[${ai}]\uFF08\u7F3A\u5C11 headlinePart1\uFF09`,
117792
117819
  status: "skipped",
117793
117820
  liveNote: "\u5E7F\u544A\u7EC4\u672A\u521B\u5EFA\uFF0C\u8DF3\u8FC7\u521B\u610F\u6BD4\u5BF9",
@@ -117799,6 +117826,7 @@ function compareCampaignCreateToLive(cfg, campaignId, live, meta) {
117799
117826
  layer: "ad",
117800
117827
  path: asset.path,
117801
117828
  adGroupName: groupName,
117829
+ adLabel,
117802
117830
  plannedContent: `${label}: ${asset.text}`,
117803
117831
  status: "skipped",
117804
117832
  liveNote: "\u5E7F\u544A\u7EC4\u672A\u521B\u5EFA\uFF0C\u8DF3\u8FC7\u6587\u6848\u6BD4\u5BF9",
@@ -117807,11 +117835,15 @@ function compareCampaignCreateToLive(cfg, campaignId, live, meta) {
117807
117835
  }
117808
117836
  continue;
117809
117837
  }
117838
+ const matchedLive = pickLiveAdForPlannedRsa(plannedHeadlines, primary, unmatchedLiveAds);
117839
+ const liveHeadlineSet = new Set(matchedLive ? collectRsaHeadlines(matchedLive) : []);
117840
+ const liveDescSet = new Set(matchedLive ? collectRsaDescriptions(matchedLive) : []);
117810
117841
  if (!primary) {
117811
117842
  pushItem(items3, {
117812
117843
  layer: "ad",
117813
117844
  path: path50,
117814
117845
  adGroupName: groupName,
117846
+ adLabel,
117815
117847
  plannedContent: `AdsForBatchJob[${ai}]\uFF08\u7F3A\u5C11 headlinePart1\uFF0C\u65E0\u6CD5\u6BD4\u5BF9\u6574\u6761 RSA\uFF09`,
117816
117848
  status: "missing",
117817
117849
  liveNote: `\u540C\u7EC4\u5DF2\u6709 ${liveAdsInGroup.length} \u6761\u521B\u610F`,
@@ -117819,41 +117851,44 @@ function compareCampaignCreateToLive(cfg, campaignId, live, meta) {
117819
117851
  });
117820
117852
  for (const asset of rsaAssets) {
117821
117853
  const label = asset.kind === "headline" ? "\u6807\u9898" : "\u63CF\u8FF0";
117822
- const pool = asset.kind === "headline" ? liveHeadlinePool : liveDescPool;
117854
+ const pool = asset.kind === "headline" ? liveHeadlineSet : liveDescSet;
117823
117855
  const found = livePoolHasText(pool, asset.text);
117824
117856
  pushItem(items3, {
117825
117857
  layer: "ad",
117826
117858
  path: asset.path,
117827
117859
  adGroupName: groupName,
117860
+ adLabel,
117828
117861
  plannedContent: `${label}: ${asset.text}`,
117829
117862
  status: found ? "ok" : "missing",
117830
- liveNote: found ? `\u540C\u7EC4 RSA \u6587\u6848\u6C60\u5DF2\u542B\u6B64${label}` : `\u540C\u7EC4 RSA \u6587\u6848\u6C60\u672A\u89C1\u6B64${label}`,
117863
+ liveNote: found ? `\u5DF2\u5728\u5BF9\u5E94 RSA \u4E0A\u627E\u5230\u6B64${label}` : `\u5BF9\u5E94 RSA \u672A\u89C1\u6B64${label}`,
117831
117864
  summary: found ? `RSA ${label}\u5DF2\u751F\u6548\uFF1A${asset.text}` : `RSA ${label}\u672A\u751F\u6548\uFF1A${asset.text}`
117832
117865
  });
117833
117866
  }
117834
117867
  continue;
117835
117868
  }
117836
- const rsaFound = liveAdsInGroup.some((la) => liveAdHasHeadline(la, primary));
117869
+ const rsaFound = matchedLive != null && liveAdHasHeadline(matchedLive, primary);
117837
117870
  pushItem(items3, {
117838
117871
  layer: "ad",
117839
117872
  path: path50,
117840
117873
  adGroupName: groupName,
117874
+ adLabel,
117841
117875
  plannedContent: `RSA \u9996\u6807\u9898: ${primary}${finalUrl ? ` | \u843D\u5730\u9875: ${finalUrl}` : ""}`,
117842
117876
  status: rsaFound ? "ok" : "missing",
117843
- liveNote: rsaFound ? `\u540C\u7EC4\u5DF2\u5339\u914D\u9996\u6807\u9898\uFF08\u5171 ${liveAdsInGroup.length} \u6761 RSA\uFF09` : `\u540C\u7EC4\u5DF2\u6709 ${liveAdsInGroup.length} \u6761 RSA\uFF0C\u65E0\u6B64\u9996\u6807\u9898`,
117844
- summary: rsaFound ? `RSA \u5DF2\u521B\u5EFA\uFF08\u9996\u6761\u6807\u9898\u300C${primary}\u300D\uFF09` : `RSA \u672A\u521B\u5EFA\uFF08\u9996\u6761\u6807\u9898\u300C${primary}\u300D\u5728\u8D26\u6237\u5185\u672A\u627E\u5230\uFF09`
117877
+ liveNote: rsaFound ? `\u5DF2\u4E00\u5BF9\u4E00\u5339\u914D\u5230\u540C\u7EC4 RSA\uFF08\u6807\u9898\u91CD\u53E0 ${overlapHeadlineCount(plannedHeadlines, matchedLive)}\uFF09` : `\u540C\u7EC4 ${liveAdsInGroup.length} \u6761 RSA \u4E2D\u65E0\u6B64\u9996\u6807\u9898\uFF0C\u6216\u5DF2\u88AB\u5176\u5B83\u8BA1\u5212\u5E7F\u544A\u8BA4\u9886`,
117878
+ summary: rsaFound ? `RSA \u5DF2\u521B\u5EFA\uFF08\u9996\u6761\u6807\u9898\u300C${primary}\u300D\uFF09` : `RSA \u672A\u521B\u5EFA\uFF08\u9996\u6761\u6807\u9898\u300C${primary}\u300D\u5728\u5BF9\u5E94\u5E7F\u544A\u4E0A\u672A\u627E\u5230\uFF09`
117845
117879
  });
117846
117880
  for (const asset of rsaAssets) {
117847
117881
  const label = asset.kind === "headline" ? "\u6807\u9898" : "\u63CF\u8FF0";
117848
- const pool = asset.kind === "headline" ? liveHeadlinePool : liveDescPool;
117882
+ const pool = asset.kind === "headline" ? liveHeadlineSet : liveDescSet;
117849
117883
  const found = livePoolHasText(pool, asset.text);
117850
117884
  pushItem(items3, {
117851
117885
  layer: "ad",
117852
117886
  path: asset.path,
117853
117887
  adGroupName: groupName,
117888
+ adLabel,
117854
117889
  plannedContent: `${label}: ${asset.text}`,
117855
117890
  status: found ? "ok" : "missing",
117856
- liveNote: found ? `\u540C\u7EC4 RSA \u6587\u6848\u6C60\u5DF2\u542B\u6B64${label}` : `\u540C\u7EC4 RSA \u6587\u6848\u6C60\u672A\u89C1\u6B64${label}\uFF08\u6C60\u5185 ${pool.size} \u6761\uFF09`,
117891
+ liveNote: found ? `\u5DF2\u5728\u5BF9\u5E94 RSA \u4E0A\u627E\u5230\u6B64${label}` : `\u5BF9\u5E94 RSA \u672A\u89C1\u6B64${label}\uFF08\u8BE5\u5E7F\u544A\u6587\u6848 ${pool.size} \u6761\uFF09`,
117857
117892
  summary: found ? `RSA ${label}\u5DF2\u751F\u6548\uFF1A${asset.text}` : `RSA ${label}\u672A\u751F\u6548\uFF1A${asset.text}`
117858
117893
  });
117859
117894
  }
@@ -118035,6 +118070,7 @@ async function fetchLiveSnapshotForCampaign(config, accountId, campaignId, campa
118035
118070
  const params = new URLSearchParams();
118036
118071
  params.set("startDate", toGoogleDate(void 0, -30));
118037
118072
  params.set("endDate", toGoogleDate(void 0, 0));
118073
+ params.set("newest", "true");
118038
118074
  const qs = params.toString();
118039
118075
  const [campaigns, adGroups, keywords, negativeKeywords, ads, extensions, targetedLocations] = await Promise.all([
118040
118076
  fetchList(
@@ -118353,13 +118389,31 @@ function buildCampaignCreateStatusMarkdown(result) {
118353
118389
  byGroup.set(key, list);
118354
118390
  }
118355
118391
  for (const [groupName, groupItems] of byGroup) {
118356
- const title = layer === "keyword" ? `## \u5E7F\u544A\u7EC4\u300C${groupName}\u300D\xB7 \u5173\u952E\u8BCD` : `## \u5E7F\u544A\u7EC4\u300C${groupName}\u300D\xB7 RSA \u6587\u6848`;
118357
- lines.push(title, ``);
118358
- lines.push(`| \u72B6\u6001 | \u8BA1\u5212\u5185\u5BB9 |`, `| --- | --- |`);
118392
+ if (layer === "keyword") {
118393
+ lines.push(`## \u5E7F\u544A\u7EC4\u300C${groupName}\u300D\xB7 \u5173\u952E\u8BCD`, ``);
118394
+ lines.push(`| \u72B6\u6001 | \u8BA1\u5212\u5185\u5BB9 |`, `| --- | --- |`);
118395
+ for (const item of groupItems) {
118396
+ lines.push(`| ${statusLabel(item.status)} | ${mdCell(item.plannedContent)} |`);
118397
+ }
118398
+ lines.push(``);
118399
+ continue;
118400
+ }
118401
+ lines.push(`## \u5E7F\u544A\u7EC4\u300C${groupName}\u300D\xB7 RSA \u6587\u6848`, ``);
118402
+ const byAd = /* @__PURE__ */ new Map();
118359
118403
  for (const item of groupItems) {
118360
- lines.push(`| ${statusLabel(item.status)} | ${mdCell(item.plannedContent)} |`);
118404
+ const key = item.adLabel ?? "\u2014";
118405
+ const list = byAd.get(key) ?? [];
118406
+ list.push(item);
118407
+ byAd.set(key, list);
118408
+ }
118409
+ for (const [adLabel, adItems] of byAd) {
118410
+ lines.push(`### \u5E7F\u544A\u300C${adLabel}\u300D`, ``);
118411
+ lines.push(`| \u72B6\u6001 | \u8BA1\u5212\u5185\u5BB9 |`, `| --- | --- |`);
118412
+ for (const item of adItems) {
118413
+ lines.push(`| ${statusLabel(item.status)} | ${mdCell(item.plannedContent)} |`);
118414
+ }
118415
+ lines.push(``);
118361
118416
  }
118362
- lines.push(``);
118363
118417
  }
118364
118418
  continue;
118365
118419
  }
@@ -136974,19 +137028,30 @@ function mapAdGroupRows(payload) {
136974
137028
  ])
136975
137029
  );
136976
137030
  }
137031
+ function displayAdTitle(row) {
137032
+ const raw = String(row.adTitle ?? "").trim();
137033
+ if (raw) return { adTitle: raw, adTitleFallback: false };
137034
+ const camp = String(row.campaignName ?? "").trim();
137035
+ const group = String(row.adGroupName ?? "").trim();
137036
+ const fallback = [camp, group].filter(Boolean).join(" / ");
137037
+ return { adTitle: fallback, adTitleFallback: fallback.length > 0 };
137038
+ }
136977
137039
  function mapAdRows(payload) {
136978
- return asRows2(payload).map(
136979
- (r) => pickMetrics(r, [
137040
+ return asRows2(payload).map((r) => {
137041
+ const mapped = pickMetrics(r, [
136980
137042
  "campaignId",
136981
137043
  "campaignName",
136982
137044
  "adGroupId",
136983
137045
  "adGroupName",
136984
137046
  "adId",
136985
- "adTitle",
136986
137047
  "adType",
136987
137048
  "adStatus"
136988
- ])
136989
- );
137049
+ ]);
137050
+ const title = displayAdTitle(r);
137051
+ mapped.adTitle = title.adTitle;
137052
+ mapped.adTitleFallback = title.adTitleFallback;
137053
+ return mapped;
137054
+ });
136990
137055
  }
136991
137056
  function mapKeywordRows(payload) {
136992
137057
  return asRows2(payload).map((r) => {
@@ -1,5 +1,5 @@
1
1
  {
2
2
  "slug": "siluzan-tso",
3
- "version": "1.1.49-beta.3",
4
- "publishedAt": 1788247766399
3
+ "version": "1.1.49-beta.4",
4
+ "publishedAt": 1788313426269
5
5
  }
@@ -55,7 +55,7 @@ siluzan-tso bing-analysis render \
55
55
  - `audienceGender[]`:`{gender, spend, impressions, clicks, ctr, averageCpc, conversions}`
56
56
  - `campaigns[]`:`{campaignName, campaignStatus, campaignStatusDisplay, spend, impressions, clicks, ctr, conversions, costPerConversion}`
57
57
  - `adGroups[]`:`{adGroupName, campaignName, qualityScore, spend, clicks, ctr, conversions, costPerConversion}`
58
- - `ads[]`:`{adTitle, adGroupName, adType, spend, clicks, ctr, conversions}`(RSA `adTitle=""`,HTML 整列为空则不渲染该列并在表上方提醒)
58
+ - `ads[]`:`{adTitle, adGroupName, adType, spend, clicks, ctr, conversions}`(RSA 的报表 `adTitle` 常为空,`render` 用「系列 / 广告组」兜底展示并在表上方注明)
59
59
  - `keywords[]`:`{keyword, matchType, qualityScore, spend, ctr, averageCpc, conversions, costPerConversion}`
60
60
  - `searchTerms[]`:`{searchQuery, keyword, deliveredMatchType, spend, ctr, conversions, costPerConversion}`
61
61
  - `narrative`(**Agent 必填,唯一由 Agent 撰写的叙事内容**):
@@ -225,7 +225,7 @@ Bing 网关常不返回 `averageDailyCost`、`activeDays`(或为 0)。CLI
225
225
  | 广告组 | `bing-analysis --sections ad-groups` | `ad-groups-*.json` |
226
226
  | 广告 | `bing-analysis --sections ads` | `ads-*.json` |
227
227
 
228
- **数据呈现**:各表按消耗降序;系列含 `campaignStatus`;广告组可含质量分相关字段(以 outline 为准)。写入 JSON `tables.campaigns[]` / `tables.adGroups[]` / `tables.ads[]`。RSA `adTitle` 常为空串,HTML 不展示空标题列。
228
+ **数据呈现**:各表按消耗降序;系列含 `campaignStatus`;广告组可含质量分相关字段(以 outline 为准)。写入 JSON `tables.campaigns[]` / `tables.adGroups[]` / `tables.ads[]`。RSA 无创意标题时 HTML 仍展示「广告标题」列,内容为「系列 / 广告组」。
229
229
 
230
230
  **分析(必写,三个子块各写一段,不可合并为一句带过)**:
231
231
 
@@ -1025,14 +1025,14 @@
1025
1025
  const currency = currencyOf(data);
1026
1026
  const section = data.narrative?.sections?.ads;
1027
1027
  const sorted = topBySpend(rows, 10);
1028
- const showTitle = sorted.some((r) => String(r.adTitle || "").trim() !== "");
1029
- const titleNotice = !showTitle && sorted.length > 0
1030
- ? `<div class="empty-note">Bing 响应式搜索广告的报表不返回单条标题,本表不展示「广告标题」列。</div>`
1028
+ const usedFallback = sorted.some((r) => r.adTitleFallback);
1029
+ const titleNotice = usedFallback
1030
+ ? `<div class="empty-note">Bing 响应式搜索广告的报表不返回创意标题,本列用「系列 / 广告组」名称代替。</div>`
1031
1031
  : "";
1032
1032
  const body = sorted
1033
1033
  .map(
1034
1034
  (r) => `<tr>
1035
- ${showTitle ? `<td>${escapeHtml(r.adTitle || "—")}</td>` : ""}
1035
+ <td>${escapeHtml(r.adTitle || "—")}</td>
1036
1036
  <td>${escapeHtml(r.adGroupName || "—")}</td>
1037
1037
  <td>${escapeHtml(r.adType || "—")}</td>
1038
1038
  <td class="num">${money(r.spend, currency)}</td>
@@ -1047,7 +1047,7 @@
1047
1047
  ? `<div class="card-title-row"><h3>广告 Top 10</h3><span class="hint">共 ${rows.length} 条广告,按消耗排序</span></div>
1048
1048
  ${titleNotice}
1049
1049
  <div class="table-wrap"><table>
1050
- <thead><tr>${showTitle ? "<th>广告标题</th>" : ""}<th>广告组</th><th>广告类型</th><th class="num">消耗</th><th class="num">点击</th><th class="num">CTR</th><th class="num">转化</th></tr></thead>
1050
+ <thead><tr><th>广告标题</th><th>广告组</th><th>广告类型</th><th class="num">消耗</th><th class="num">点击</th><th class="num">CTR</th><th class="num">转化</th></tr></thead>
1051
1051
  <tbody>${body}</tbody>
1052
1052
  </table></div>`
1053
1053
  : emptyNoteHtml();
@@ -55,7 +55,7 @@ siluzan-tso bing-analysis render \
55
55
  - `audienceGender[]`:`{gender, spend, impressions, clicks, ctr, averageCpc, conversions}`
56
56
  - `campaigns[]`:`{campaignName, campaignStatus, campaignStatusDisplay, spend, impressions, clicks, ctr, conversions, costPerConversion}`
57
57
  - `adGroups[]`:`{adGroupName, campaignName, qualityScore, spend, clicks, ctr, conversions, costPerConversion}`
58
- - `ads[]`:`{adTitle, adGroupName, adType, spend, clicks, ctr, conversions}`(RSA `adTitle=""`,HTML 整列为空则不渲染该列并在表上方提醒)
58
+ - `ads[]`:`{adTitle, adGroupName, adType, spend, clicks, ctr, conversions}`(RSA 的报表 `adTitle` 常为空,`render` 用「系列 / 广告组」兜底展示并在表上方注明)
59
59
  - `keywords[]`:`{keyword, matchType, qualityScore, spend, ctr, averageCpc, conversions, costPerConversion}`
60
60
  - `searchTerms[]`:`{searchQuery, keyword, deliveredMatchType, spend, ctr, conversions, costPerConversion}`
61
61
  - `narrative`(**Agent 必填,唯一由 Agent 撰写的叙事内容**):
@@ -225,7 +225,7 @@ Bing 网关常不返回 `averageDailyCost`、`activeDays`(或为 0)。CLI
225
225
  | 广告组 | `bing-analysis --sections ad-groups` | `ad-groups-*.json` |
226
226
  | 广告 | `bing-analysis --sections ads` | `ads-*.json` |
227
227
 
228
- **数据呈现**:各表按消耗降序;系列含 `campaignStatus`;广告组可含质量分相关字段(以 outline 为准)。写入 JSON `tables.campaigns[]` / `tables.adGroups[]` / `tables.ads[]`。RSA `adTitle` 常为空串,HTML 不展示空标题列。
228
+ **数据呈现**:各表按消耗降序;系列含 `campaignStatus`;广告组可含质量分相关字段(以 outline 为准)。写入 JSON `tables.campaigns[]` / `tables.adGroups[]` / `tables.ads[]`。RSA 无创意标题时 HTML 仍展示「广告标题」列,内容为「系列 / 广告组」。
229
229
 
230
230
  **分析(必写,三个子块各写一段,不可合并为一句带过)**:
231
231
 
@@ -9,7 +9,7 @@ $ErrorActionPreference = 'Stop'
9
9
  # -- Package info (injected at build time) ------------------------------------
10
10
  $PKG_NAME = 'siluzan-tso-cli'
11
11
  # PKG_VERSION 锁定到与本脚本同批构建产物一致的版本,避免与 dist/skill 错位
12
- $PKG_VERSION = '1.1.49-beta.3'
12
+ $PKG_VERSION = '1.1.49-beta.4'
13
13
  $CLI_BIN = 'siluzan-tso'
14
14
  $SKILL_LABEL = 'Siluzan TSO'
15
15
  $INSTALL_CMD = 'npm install -g siluzan-tso-cli@beta'
@@ -9,7 +9,7 @@ set -euo pipefail
9
9
  # -- Package info (injected at build time) ------------------------------------
10
10
  readonly PKG_NAME="siluzan-tso-cli"
11
11
  # PKG_VERSION 锁定到与本脚本同批构建产物一致的版本,避免与 dist/skill 错位
12
- readonly PKG_VERSION="1.1.49-beta.3"
12
+ readonly PKG_VERSION="1.1.49-beta.4"
13
13
  readonly CLI_BIN="siluzan-tso"
14
14
  readonly SKILL_LABEL="Siluzan TSO"
15
15
  readonly INSTALL_CMD="npm install -g siluzan-tso-cli@beta"
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "siluzan-tso-cli",
3
- "version": "1.1.49-beta.3",
3
+ "version": "1.1.49-beta.4",
4
4
  "description": "Siluzan 广告账户管理 CLI — 查询账户、余额、消耗数据,管理绑定关系与充值。",
5
5
  "keywords": [
6
6
  "ad-account",