siluzan-tso-cli 1.1.32 → 1.1.33-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/README.md CHANGED
@@ -43,7 +43,7 @@ HTML 报告模板外部资源:**ECharts** 统一使用 `https://staticpn.siluz
43
43
  在**用户的目标项目根目录**执行(根据用户使用的助手选择 `--ai`):
44
44
 
45
45
  ```bash
46
- npm install -g siluzan-tso-cli
46
+ npm install -g siluzan-tso-cli@beta
47
47
  siluzan-tso init --ai cursor # 写入 Cursor(默认)
48
48
  siluzan-tso init --ai cursor,claude # 同时写入多个平台
49
49
  siluzan-tso init --ai all # 写入所有支持的平台
@@ -51,6 +51,7 @@ siluzan-tso init -d /path/to/skills # 写入自定义目录
51
51
  siluzan-tso init --force # 强制覆盖已存在文件
52
52
  ```
53
53
 
54
+ > **注意**:当前为测试版(1.1.33-beta.2),供内部测试使用。正式发布后安装命令将改为 `npm install -g siluzan-tso-cli`。
54
55
 
55
56
  | 助手 | 建议 `--ai` |
56
57
  | ----------------------- | ------------------------------------ |
package/dist/index.js CHANGED
@@ -1980,6 +1980,7 @@ import * as fs3 from "fs/promises";
1980
1980
  import * as path3 from "path";
1981
1981
  import * as fs4 from "fs/promises";
1982
1982
  import * as path4 from "path";
1983
+ import { spawn as spawn2 } from "child_process";
1983
1984
  import * as path5 from "path";
1984
1985
  import { chmod, mkdir as mkdir3, open, readdir, stat, unlink } from "fs/promises";
1985
1986
  import * as path6 from "path";
@@ -2933,6 +2934,60 @@ function installProcessHandlers() {
2933
2934
  process.exit(1);
2934
2935
  });
2935
2936
  }
2937
+ function tryOpenHttpsUrlInBrowser(url) {
2938
+ try {
2939
+ const parsed = new URL(url);
2940
+ if (parsed.protocol !== "https:") {
2941
+ return false;
2942
+ }
2943
+ if (process.platform === "win32") {
2944
+ const child = spawn2("rundll32", ["url.dll,FileProtocolHandler", parsed.toString()], {
2945
+ detached: true,
2946
+ stdio: "ignore",
2947
+ windowsHide: true
2948
+ });
2949
+ child.unref();
2950
+ } else if (process.platform === "darwin") {
2951
+ const child = spawn2("open", [parsed.toString()], { detached: true, stdio: "ignore" });
2952
+ child.unref();
2953
+ } else {
2954
+ const child = spawn2("xdg-open", [parsed.toString()], { detached: true, stdio: "ignore" });
2955
+ child.unref();
2956
+ }
2957
+ return true;
2958
+ } catch {
2959
+ return false;
2960
+ }
2961
+ }
2962
+ function deliverMediaAccountOAuthLink(opts) {
2963
+ const { mediaLabel, redirectUrl, tryOpenBrowser: tryOpenBrowser2 } = opts;
2964
+ if (isSiluzanAgentEnv()) {
2965
+ console.log(
2966
+ `
2967
+ \u2139\uFE0F \u68C0\u6D4B\u5230 Siluzan Agent \u73AF\u5883\uFF08IS_SILUZAN_AGENT_ENV=true\uFF09\u3002
2968
+ \u8BF7\u6253\u5F00\u4EE5\u4E0B\u94FE\u63A5\u5B8C\u6210 ${mediaLabel} \u5E7F\u544A\u8D26\u6237\u6388\u6743\uFF08\u6C99\u7BB1\u5185\u4E0D\u4F1A\u81EA\u52A8\u6253\u5F00\u6D4F\u89C8\u5668\uFF09\uFF1A
2969
+
2970
+ ${redirectUrl}
2971
+ `
2972
+ );
2973
+ return;
2974
+ }
2975
+ if (tryOpenBrowser2) {
2976
+ const opened = tryOpenBrowser2(redirectUrl);
2977
+ if (opened) {
2978
+ console.log(`
2979
+ \u2705 \u5DF2\u5728\u9ED8\u8BA4\u6D4F\u89C8\u5668\u4E2D\u6253\u5F00 ${mediaLabel} \u6388\u6743\u9875\u9762\u3002`);
2980
+ console.log(` \u5B8C\u6210\u6388\u6743\u540E\u6D4F\u89C8\u5668\u4F1A\u81EA\u52A8\u8DF3\u56DE\u4E1D\u8DEF\u8D5E\uFF0C\u8D26\u6237\u7ED1\u5B9A\u5373\u751F\u6548\u3002
2981
+ `);
2982
+ } else {
2983
+ console.log(`
2984
+ \u26A0\uFE0F \u65E0\u6CD5\u81EA\u52A8\u6253\u5F00\u6D4F\u89C8\u5668\uFF0C\u8BF7\u624B\u52A8\u590D\u5236\u4EE5\u4E0B\u94FE\u63A5\u5230\u6D4F\u89C8\u5668\u4E2D\u5B8C\u6210\u6388\u6743\uFF1A
2985
+ `);
2986
+ }
2987
+ }
2988
+ console.log(` ${redirectUrl}
2989
+ `);
2990
+ }
2936
2991
  function printAuthMissingHelp(binName) {
2937
2992
  if (isSiluzanAgentEnv()) {
2938
2993
  console.error(
@@ -3774,7 +3829,7 @@ var DEFAULT_API_BASE;
3774
3829
  var init_defaults = __esm({
3775
3830
  "src/config/defaults.ts"() {
3776
3831
  "use strict";
3777
- DEFAULT_API_BASE = "https://tso-api.siluzan.com";
3832
+ DEFAULT_API_BASE = "https://tso-api-ci.siluzan.com";
3778
3833
  }
3779
3834
  });
3780
3835
 
@@ -4274,6 +4329,44 @@ function groupAccountIdsByOverviewRoute(cliMedia, accountIds, accountMediaTypes)
4274
4329
  }
4275
4330
  return groups;
4276
4331
  }
4332
+ function routeToBalanceMedia(route) {
4333
+ if (route === "FacebookAds") return "MetaAd";
4334
+ if (BALANCE_SUPPORTED_MEDIA.includes(route)) {
4335
+ return route;
4336
+ }
4337
+ return null;
4338
+ }
4339
+ function applyRealtimeBalanceIfNeeded(item, bal) {
4340
+ if (bal == null || typeof bal.remainingAccountBudget !== "number") return;
4341
+ if (bal.remainingAccountBudget > 0) {
4342
+ item.remainingAccountBudget = bal.remainingAccountBudget;
4343
+ }
4344
+ if (bal.currencyCode) item.currencyCode = bal.currencyCode;
4345
+ if (bal.status) item.status = bal.status;
4346
+ }
4347
+ async function overlayRealtimeBalanceOnOverviewMap(route, overviewMap, config, verbose) {
4348
+ const media = routeToBalanceMedia(route);
4349
+ if (!media || overviewMap.size === 0) return;
4350
+ const ids = [...overviewMap.keys()];
4351
+ const balanceMap = await fetchBalanceMap(media, ids, config, void 0, void 0, verbose);
4352
+ for (const [id, item] of overviewMap) {
4353
+ applyRealtimeBalanceIfNeeded(item, balanceMap.get(id));
4354
+ }
4355
+ }
4356
+ async function enrichOverviewRecordWithRealtimeBalance(media, accountId, config, record, verbose) {
4357
+ if (!BALANCE_SUPPORTED_MEDIA.includes(media)) {
4358
+ return record;
4359
+ }
4360
+ const balanceMap = await fetchBalanceMap(media, [accountId], config, void 0, void 0, verbose);
4361
+ const bal = balanceMap.get(accountId);
4362
+ if (bal == null) return record;
4363
+ const out = { ...record };
4364
+ applyRealtimeBalanceIfNeeded(out, bal);
4365
+ if (bal.name && out["accountName"] == null && out["mediaCustomerName"] == null) {
4366
+ out["mediaCustomerName"] = bal.name;
4367
+ }
4368
+ return out;
4369
+ }
4277
4370
  async function fetchBalanceMap(media, accountIds, config, startDate, endDate, verbose) {
4278
4371
  const result = /* @__PURE__ */ new Map();
4279
4372
  if (accountIds.length === 0) return result;
@@ -4407,70 +4500,73 @@ async function fetchOverviewMap(route, accountIds, config, startDate, endDate, v
4407
4500
  for (const row of rows) {
4408
4501
  result.set(row.mediaAccountId, row);
4409
4502
  }
4410
- return result;
4411
- }
4412
- const fetchLegacyBatch = async (ids) => {
4413
- const batch = /* @__PURE__ */ new Map();
4414
- const params = new URLSearchParams({
4415
- period: "true",
4416
- startDate: start,
4417
- endDate: end,
4418
- mediaCustomerIds: ids.join(",")
4419
- });
4420
- const url = `${config.apiBaseUrl}/report/media-account/${route}/accountsoverview?${params}`;
4421
- const raw = await apiFetch2(url, config, {}, verbose);
4422
- const items = Array.isArray(raw) ? raw : [];
4423
- for (const item of items) {
4424
- if (item.mediaAccountId != null) {
4425
- batch.set(String(item.mediaAccountId), item);
4503
+ } else {
4504
+ const fetchLegacyBatch = async (ids) => {
4505
+ const batch = /* @__PURE__ */ new Map();
4506
+ const params = new URLSearchParams({
4507
+ period: "true",
4508
+ startDate: start,
4509
+ endDate: end,
4510
+ mediaCustomerIds: ids.join(",")
4511
+ });
4512
+ const url = `${config.apiBaseUrl}/report/media-account/${route}/accountsoverview?${params}`;
4513
+ const raw = await apiFetch2(url, config, {}, verbose);
4514
+ const items = Array.isArray(raw) ? raw : [];
4515
+ for (const item of items) {
4516
+ if (item.mediaAccountId != null) {
4517
+ batch.set(String(item.mediaAccountId), item);
4518
+ }
4426
4519
  }
4427
- }
4428
- return batch;
4429
- };
4430
- const mergeOverview = (batch) => {
4431
- for (const [k, v] of batch) result.set(k, v);
4432
- };
4433
- if (accountIds.length === 1) {
4434
- mergeOverview(await fetchLegacyBatch(accountIds));
4435
- return result;
4436
- }
4437
- try {
4438
- const batch = await fetchLegacyBatch(accountIds);
4439
- mergeOverview(batch);
4440
- if (batch.size === 0) {
4441
- for (const id of accountIds) {
4442
- try {
4443
- mergeOverview(await fetchLegacyBatch([id]));
4444
- } catch (singleErr) {
4445
- if (verbose) {
4446
- process.stderr.write(
4447
- `[fetchOverviewMap] \u9010\u6237\u91CD\u8BD5\u5931\u8D25 ${id}\uFF1A${singleErr instanceof Error ? singleErr.message : String(singleErr)}
4520
+ return batch;
4521
+ };
4522
+ const mergeOverview = (batch) => {
4523
+ for (const [k, v] of batch) result.set(k, v);
4524
+ };
4525
+ if (accountIds.length === 1) {
4526
+ mergeOverview(await fetchLegacyBatch(accountIds));
4527
+ } else {
4528
+ try {
4529
+ const batch = await fetchLegacyBatch(accountIds);
4530
+ mergeOverview(batch);
4531
+ if (batch.size === 0) {
4532
+ for (const id of accountIds) {
4533
+ try {
4534
+ mergeOverview(await fetchLegacyBatch([id]));
4535
+ } catch (singleErr) {
4536
+ if (verbose) {
4537
+ process.stderr.write(
4538
+ `[fetchOverviewMap] \u9010\u6237\u91CD\u8BD5\u5931\u8D25 ${id}\uFF1A${singleErr instanceof Error ? singleErr.message : String(singleErr)}
4448
4539
  `
4449
- );
4540
+ );
4541
+ }
4542
+ }
4450
4543
  }
4451
4544
  }
4452
- }
4453
- }
4454
- } catch (err) {
4455
- if (verbose) {
4456
- process.stderr.write(
4457
- `[fetchOverviewMap] \u6279\u91CF\u5F02\u5E38\uFF0C\u9010\u6237\u91CD\u8BD5\uFF1A${err instanceof Error ? err.message : String(err)}
4458
- `
4459
- );
4460
- }
4461
- for (const id of accountIds) {
4462
- try {
4463
- mergeOverview(await fetchLegacyBatch([id]));
4464
- } catch (singleErr) {
4545
+ } catch (err) {
4465
4546
  if (verbose) {
4466
4547
  process.stderr.write(
4467
- `[fetchOverviewMap] \u9010\u6237\u91CD\u8BD5\u5931\u8D25 ${id}\uFF1A${singleErr instanceof Error ? singleErr.message : String(singleErr)}
4548
+ `[fetchOverviewMap] \u6279\u91CF\u5F02\u5E38\uFF0C\u9010\u6237\u91CD\u8BD5\uFF1A${err instanceof Error ? err.message : String(err)}
4468
4549
  `
4469
4550
  );
4470
4551
  }
4552
+ for (const id of accountIds) {
4553
+ try {
4554
+ mergeOverview(await fetchLegacyBatch([id]));
4555
+ } catch (singleErr) {
4556
+ if (verbose) {
4557
+ process.stderr.write(
4558
+ `[fetchOverviewMap] \u9010\u6237\u91CD\u8BD5\u5931\u8D25 ${id}\uFF1A${singleErr instanceof Error ? singleErr.message : String(singleErr)}
4559
+ `
4560
+ );
4561
+ }
4562
+ }
4563
+ }
4471
4564
  }
4472
4565
  }
4473
4566
  }
4567
+ if (result.size > 0) {
4568
+ await overlayRealtimeBalanceOnOverviewMap(route, result, config, verbose);
4569
+ }
4474
4570
  } catch (err) {
4475
4571
  if (verbose) {
4476
4572
  process.stderr.write(
@@ -106155,11 +106251,15 @@ async function runStats(opts) {
106155
106251
  }
106156
106252
  }
106157
106253
  } else {
106158
- const params = new URLSearchParams({ startDate, endDate, period: "true" });
106159
- params.set("mediaCustomerIds", ids.join(","));
106160
- const url = `${config.apiBaseUrl}/report/media-account/${opts.media}/accountsoverview?${params.toString()}`;
106161
- const raw = await apiFetch2(url, config, {}, opts.verbose);
106162
- items = Array.isArray(raw) ? raw : raw.items ?? [];
106254
+ const map = await fetchOverviewMap(
106255
+ opts.media,
106256
+ ids,
106257
+ config,
106258
+ startDate,
106259
+ endDate,
106260
+ opts.verbose
106261
+ );
106262
+ items = [...map.values()];
106163
106263
  }
106164
106264
  } catch (err) {
106165
106265
  const message = err instanceof Error ? err.message : String(err);
@@ -107693,6 +107793,7 @@ async function fetchBingJson(config, url, verbose) {
107693
107793
 
107694
107794
  // src/commands/report-bing-analysis/run.ts
107695
107795
  init_auth();
107796
+ init_balance();
107696
107797
  async function runReportBingOverview(opts) {
107697
107798
  const id = assertBingAccountId(opts.account);
107698
107799
  const { startDate, endDate } = resolveBingDateRange(opts.start, opts.end);
@@ -107700,7 +107801,14 @@ async function runReportBingOverview(opts) {
107700
107801
  const params = new URLSearchParams({ startDate, endDate });
107701
107802
  const url = reportingUrl2(config, id, "OverviewSectionData", params.toString());
107702
107803
  try {
107703
- const data = await fetchBingJson(config, url, !!opts.verbose);
107804
+ 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;
107704
107812
  if (opts.jsonOut) {
107705
107813
  await emitBingSnapshot(opts, "bing-overview", id, startDate, endDate, data);
107706
107814
  return;
@@ -120909,35 +121017,11 @@ async function runAccountShareDetail(opts) {
120909
121017
 
120910
121018
  // src/commands/account-manage/oauth.ts
120911
121019
  init_auth();
120912
- import { spawn as spawn2 } from "child_process";
121020
+ init_dist();
120913
121021
  function toApiMediaType(media) {
120914
121022
  return media === "Meta" ? "FacebookAds" : media;
120915
121023
  }
120916
- function tryOpenBrowser(url) {
120917
- try {
120918
- const parsed = new URL(url);
120919
- if (parsed.protocol !== "https:") {
120920
- return false;
120921
- }
120922
- if (process.platform === "win32") {
120923
- const child = spawn2("rundll32", ["url.dll,FileProtocolHandler", parsed.toString()], {
120924
- detached: true,
120925
- stdio: "ignore",
120926
- windowsHide: true
120927
- });
120928
- child.unref();
120929
- } else if (process.platform === "darwin") {
120930
- const child = spawn2("open", [parsed.toString()], { detached: true, stdio: "ignore" });
120931
- child.unref();
120932
- } else {
120933
- const child = spawn2("xdg-open", [parsed.toString()], { detached: true, stdio: "ignore" });
120934
- child.unref();
120935
- }
120936
- return true;
120937
- } catch {
120938
- return false;
120939
- }
120940
- }
121024
+ var tryOpenBrowser = tryOpenHttpsUrlInBrowser;
120941
121025
  async function runAccountAuth(opts) {
120942
121026
  const config = loadConfig(opts.token);
120943
121027
  const apiMediaType = toApiMediaType(opts.media);
@@ -120963,17 +121047,32 @@ async function runAccountAuth(opts) {
120963
121047
  `);
120964
121048
  process.exit(1);
120965
121049
  }
120966
- const opened = tryOpenBrowser(redirectUrl);
120967
- if (opened) {
120968
- console.log(`\u2705 \u5DF2\u5728\u9ED8\u8BA4\u6D4F\u89C8\u5668\u4E2D\u6253\u5F00 ${opts.media} \u6388\u6743\u9875\u9762\u3002`);
120969
- console.log(` \u5B8C\u6210\u6388\u6743\u540E\u6D4F\u89C8\u5668\u4F1A\u81EA\u52A8\u8DF3\u56DE\u4E1D\u8DEF\u8D5E\uFF0C\u8D26\u6237\u7ED1\u5B9A\u5373\u751F\u6548\u3002
120970
- `);
120971
- } else {
120972
- console.log(`\u26A0\uFE0F \u65E0\u6CD5\u81EA\u52A8\u6253\u5F00\u6D4F\u89C8\u5668\uFF0C\u8BF7\u624B\u52A8\u590D\u5236\u4EE5\u4E0B\u94FE\u63A5\u5230\u6D4F\u89C8\u5668\u4E2D\u5B8C\u6210\u6388\u6743\uFF1A
120973
- `);
121050
+ deliverMediaAccountOAuthLink({
121051
+ mediaLabel: opts.media,
121052
+ redirectUrl,
121053
+ tryOpenBrowser: tryOpenHttpsUrlInBrowser
121054
+ });
121055
+ }
121056
+ async function runAccountReauth(opts) {
121057
+ if (!opts.id && (!opts.ids || opts.ids.length === 0)) {
121058
+ console.error(
121059
+ "\n\u274C \u91CD\u65B0\u6388\u6743\u987B\u6307\u5B9A\u8981\u89E3\u7ED1\u7684\u8D26\u6237 entityId\uFF1A--id <entityId> \u6216 --ids id1,id2\n entityId \u6765\u81EA list-accounts --json-out \u7684 ma.entityId\uFF08\u4E0D\u662F mediaCustomerId\uFF09\n"
121060
+ );
121061
+ process.exit(1);
120974
121062
  }
120975
- console.log(` ${redirectUrl}
120976
- `);
121063
+ console.log("\n\u6B65\u9AA4 1/2\uFF1A\u65AD\u5F00\u5173\u8054\uFF08\u91CD\u65B0\u6388\u6743\u524D\u5FC5\u987B\u5148\u89E3\u7ED1\uFF0C\u5BF9\u9F50\u7F51\u9875\u300C\u91CD\u65B0\u6388\u6743\u300D\uFF09\u2026\n");
121064
+ await runAccountDelink({
121065
+ token: opts.token,
121066
+ id: opts.id,
121067
+ ids: opts.ids,
121068
+ verbose: opts.verbose
121069
+ });
121070
+ console.log("\u6B65\u9AA4 2/2\uFF1A\u53D1\u8D77 OAuth \u6388\u6743\u2026\n");
121071
+ await runAccountAuth({
121072
+ token: opts.token,
121073
+ media: opts.media,
121074
+ verbose: opts.verbose
121075
+ });
120977
121076
  }
120978
121077
 
120979
121078
  // src/commands/account-manage/google-mcc.ts
@@ -121934,12 +122033,30 @@ function register23(program2) {
121934
122033
  });
121935
122034
  }
121936
122035
  );
121937
- accountCmd.command("auth").description("\u6DFB\u52A0\u6388\u6743\uFF1A\u53D1\u8D77\u5A92\u4F53 OAuth\uFF0C\u6D4F\u89C8\u5668\u6253\u5F00\u6388\u6743\u9875\uFF08\u7F51\u9875 manageAccounts\u300C\u6DFB\u52A0\u6388\u6743\u300D\uFF09").requiredOption(
122036
+ accountCmd.command("auth").description(
122037
+ "\u6DFB\u52A0\u6388\u6743\uFF1A\u9996\u6B21\u7ED1\u5B9A\u5A92\u4F53\u8D26\u6237 OAuth\uFF08\u7F51\u9875 manageAccounts\u300C\u6DFB\u52A0\u6388\u6743\u300D\uFF09\uFF1BOAuth \u5931\u6548\u8BF7\u7528 reauth"
122038
+ ).requiredOption(
121938
122039
  "-m, --media <type>",
121939
122040
  "\u5A92\u4F53\u7C7B\u578B\uFF1AGoogle | TikTok | Meta | Yandex | BingV2 | Kwai"
121940
122041
  ).option("-t, --token <token>", "Auth Token").option("--verbose", "\u8BE6\u7EC6\u9519\u8BEF\u4FE1\u606F", false).action(async (opts) => {
121941
122042
  await runAccountAuth({ token: opts.token, media: opts.media, verbose: opts.verbose });
121942
122043
  });
122044
+ accountCmd.command("reauth").description(
122045
+ "\u91CD\u65B0\u6388\u6743\uFF1A\u5148\u89E3\u7ED1\u518D OAuth\uFF08\u7F51\u9875 manageAccounts\u300C\u91CD\u65B0\u6388\u6743\u300D\uFF1BinvalidOAuthToken \u5931\u6548\u65F6\u5FC5\u8D70\u6B64\u547D\u4EE4\uFF09"
122046
+ ).requiredOption(
122047
+ "-m, --media <type>",
122048
+ "\u5A92\u4F53\u7C7B\u578B\uFF1AGoogle | TikTok | Meta | Yandex | BingV2 | Kwai"
122049
+ ).option("--id <entityId>", "\u5355\u4E2A\u8D26\u6237 entityId\uFF08\u6765\u81EA list-accounts \u7684 ma.entityId\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(
122050
+ async (opts) => {
122051
+ await runAccountReauth({
122052
+ token: opts.token,
122053
+ media: opts.media,
122054
+ id: opts.id,
122055
+ ids: opts.ids ? opts.ids.split(",").map((s) => s.trim()).filter(Boolean) : void 0,
122056
+ verbose: opts.verbose
122057
+ });
122058
+ }
122059
+ );
121943
122060
  accountCmd.command("mcc-bind").description("Google\uFF1A\u5C06\u5B50\u8D26\u6237\u7ED1\u5B9A\u5230\u7ECF\u7406\u8D26\u6237\uFF08MCC\uFF09\uFF0C\u9700\u5DF2\u914D\u7F6E googleApiUrl").requiredOption(
121944
122061
  "--customers <ids>",
121945
122062
  "\u5B50\u8D26\u6237 mediaCustomerId\uFF0C\u591A\u4E2A\u9017\u53F7\u5206\u9694\uFF08\u6765\u81EA list-accounts \u7684 ma.mediaCustomerId\uFF09"
@@ -1,5 +1,5 @@
1
1
  {
2
2
  "slug": "siluzan-tso",
3
- "version": "1.1.32",
4
- "publishedAt": 1782807133114
3
+ "version": "1.1.33-beta.2",
4
+ "publishedAt": 1782876066592
5
5
  }
@@ -280,7 +280,7 @@ siluzan-tso account-history --start 2026-03-01 --end 2026-03-31 --json-out ./sna
280
280
  | 状态 | 含义 | 下一步操作 |
281
281
  | ---------- | -------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
282
282
  | `Pending` | 审核中 | 等待,可反复运行此命令轮询;审核周期因媒体而异 |
283
- | `Approved` | 审核通过 | 运行 `list-accounts -m <媒体>` 确认账户已出现;引导用户充值激活(`config show` 取 `webUrl`,按 `finance.md` 打开对应媒体充值页;例如 Google 为 `https://www.siluzan.com/v3/foreign_trade/tso/recharge/pay?mediaType=Google`;Kwai、Yandex 当前没有对应充值界面) |
283
+ | `Approved` | 审核通过 | 运行 `list-accounts -m <媒体>` 确认账户已出现;引导用户充值激活(`config show` 取 `webUrl`,按 `finance.md` 打开对应媒体充值页;例如 Google 为 `https://www-ci.siluzan.com/v3/foreign_trade/tso/recharge/pay?mediaType=Google`;Kwai、Yandex 当前没有对应充值界面) |
284
284
  | `Rejected` | 被拒 | 查看 `--json-out` 落盘中的 `reason` 字段了解拒绝原因;修改资料后重新提交;若原因不明,引导用户联系丝路赞客服 |
285
285
 
286
286
  ---
@@ -307,9 +307,11 @@ siluzan-tso account me --check-phone 15130150466 --json-out ./snap-me
307
307
 
308
308
  ---
309
309
 
310
- ### auth — 添加媒体平台 OAuth 授权
310
+ ### auth — 添加媒体平台 OAuth 授权(首次绑定)
311
311
 
312
- 在浏览器中打开对应媒体的 OAuth 授权页面,授权后账户自动绑定到丝路赞。
312
+ 在浏览器中打开对应媒体的 OAuth 授权页面,授权后账户自动绑定到丝路赞。对应网页 **「添加授权」**。
313
+
314
+ > **OAuth 已失效**(`invalidOAuthToken=true`)时**不要**用本命令,须走下方 **`reauth`**(重新授权必须先解绑)。
313
315
 
314
316
  ```bash
315
317
  siluzan-tso account auth -m <媒体类型>
@@ -322,13 +324,13 @@ siluzan-tso account auth -m <媒体类型>
322
324
  **示例:**
323
325
 
324
326
  ```bash
325
- # 授权 Google Ads 账户
327
+ # 首次授权 Google Ads 账户
326
328
  siluzan-tso account auth -m Google
327
329
 
328
- # 授权 TikTok Ads 账户
330
+ # 首次授权 TikTok Ads 账户
329
331
  siluzan-tso account auth -m TikTok
330
332
 
331
- # 授权 Meta(Facebook)Ads 账户
333
+ # 首次授权 Meta(Facebook)Ads 账户
332
334
  siluzan-tso account auth -m Meta
333
335
  ```
334
336
 
@@ -336,6 +338,38 @@ siluzan-tso account auth -m Meta
336
338
 
337
339
  ---
338
340
 
341
+ ### reauth — 重新授权(先解绑再 OAuth)
342
+
343
+ OAuth 失效时恢复授权。对齐 TSO 网页 **「重新授权」**:程序会先 **delink** 断开关联,再跳转媒体 OAuth。**禁止**对失效账户跳过解绑直接用 `account auth`。
344
+
345
+ ```bash
346
+ siluzan-tso account reauth -m <媒体类型> --id <entityId>
347
+ siluzan-tso account reauth -m Google --ids <id1,id2>
348
+ ```
349
+
350
+ | 选项 | 说明 |
351
+ | -------------------- | ------------------------------------------------------------------------ |
352
+ | `-m, --media <type>` | 媒体类型(必填) |
353
+ | `--id <entityId>` | 单个账户 `entityId`(来自 `list-accounts` 的 `ma.entityId`) |
354
+ | `--ids <id1,id2>` | 批量 `entityId`,逗号分隔(与 `--id` 二选一) |
355
+
356
+ **示例:**
357
+
358
+ ```bash
359
+ # 1. 查失效账户的 entityId
360
+ siluzan-tso list-accounts -m Google --json-out ./snap
361
+
362
+ # 2. 重新授权(内置 delink → OAuth)
363
+ siluzan-tso account reauth -m Google --id abc123def456
364
+
365
+ # 3. 验证
366
+ siluzan-tso list-accounts -m Google
367
+ ```
368
+
369
+ > 手动两步等价于 `reauth`:`account delink --id …` → `account auth -m …`(须用户确认解绑风险)。
370
+
371
+ ---
372
+
339
373
  ### delink — 解除授权 / 断开账户关联
340
374
 
341
375
  从当前丝路赞账号下移除媒体账户绑定。
@@ -854,7 +888,7 @@ siluzan-tso account email-deauth -c <mediaCustomerId> --invitation-id <id> --res
854
888
 
855
889
  | 功能 | 媒体 | 网页路径 |
856
890
  | --------------------------------------- | ------ | ------------------------------------------------- |
857
- | **账户激活**(邀请他人激活 / 充值激活) | Google | `https://www.siluzan.com/v3/foreign_trade/tso/manageAccounts` |
891
+ | **账户激活**(邀请他人激活 / 充值激活) | Google | `https://www-ci.siluzan.com/v3/foreign_trade/tso/manageAccounts` |
858
892
 
859
893
  **Agent 建议话术**:
860
894
 
@@ -863,5 +897,5 @@ siluzan-tso account email-deauth -c <mediaCustomerId> --invitation-id <id> --res
863
897
  siluzan-tso config show # 查看 webUrl 字段
864
898
 
865
899
  # 账户激活(Google)→ 引导至账户管理页
866
- # https://www.siluzan.com/v3/foreign_trade/tso/manageAccounts
900
+ # https://www-ci.siluzan.com/v3/foreign_trade/tso/manageAccounts
867
901
  ```
@@ -117,7 +117,7 @@ siluzan-tso config show
117
117
 
118
118
  ### 充值页链接(按媒体 × 类型)
119
119
 
120
- 链接模式:`https://www.siluzan.com/v3/foreign_trade/tso/recharge/<page>?mediaType=<mediaType>`;丝路赞钱包:`https://www.siluzan.com/v3/foreign_trade/tso/recharge/siluzanWallet`(无媒体参数)。
120
+ 链接模式:`https://www-ci.siluzan.com/v3/foreign_trade/tso/recharge/<page>?mediaType=<mediaType>`;丝路赞钱包:`https://www-ci.siluzan.com/v3/foreign_trade/tso/recharge/siluzanWallet`(无媒体参数)。
121
121
 
122
122
  | 充值类型 | `<page>` | 支持媒体(`mediaType` 参数) |
123
123
  | ------------------------- | --------------------- | ------------------------------------------- |
@@ -1,6 +1,6 @@
1
1
  # 各媒体开户
2
2
 
3
- > 网页链接:`https://www.siluzan.com/v3/foreign_trade/tso/accountOpeningHistory?tso=%2Fv3umijs%2Ftso%2FaccountOpeningHistory`
3
+ > 网页链接:`https://www-ci.siluzan.com/v3/foreign_trade/tso/accountOpeningHistory?tso=%2Fv3umijs%2Ftso%2FaccountOpeningHistory`
4
4
  > 多命令串联见 `references/core/workflows.md` § 流程一。
5
5
 
6
6
  ## 首次响应硬规范(必读)
@@ -60,7 +60,7 @@ siluzan-tso open-account google-wizard
60
60
 
61
61
  ```bash
62
62
  siluzan-tso account-history -m Google
63
- # 审核通过后:config show → https://www.siluzan.com/v3/foreign_trade/tso/recharge/pay?mediaType=Google
63
+ # 审核通过后:config show → https://www-ci.siluzan.com/v3/foreign_trade/tso/recharge/pay?mediaType=Google
64
64
  ```
65
65
 
66
66
  ---
@@ -1,6 +1,6 @@
1
1
  # rag:知识库检索(TSO 广告投放辅助)
2
2
 
3
- 知识库管理页面在 https://www.siluzan.com/knowledge-base/
3
+ 知识库管理页面在 https://www-ci.siluzan.com/knowledge-base/
4
4
 
5
5
  为 **广告投放、账户分析、拓词、诊断报告** 等 TSO 业务提供**企业已入库**的产品、行业、客户背景事实依据。
6
6
 
@@ -192,9 +192,9 @@ siluzan-tso report push receive-emails -m Google [--json-out ./snap]
192
192
 
193
193
  | 媒体 | 报告类型 | URL 模板 |
194
194
  | ------ | ---------------- | ----------------------------------------------------------- |
195
- | Google | 日报(Daily) | `https://www.siluzan.com/media-report/publish/{entityId}?culture=zh-CN` |
196
- | Google | 小时报(Hourly) | `https://www.siluzan.com/media-report/hour/{entityId}?culture=zh-CN` |
197
- | TikTok | 日报 | `https://www.siluzan.com/media-report/publish/{entityId}?culture=zh-CN` |
195
+ | Google | 日报(Daily) | `https://www-ci.siluzan.com/media-report/publish/{entityId}?culture=zh-CN` |
196
+ | Google | 小时报(Hourly) | `https://www-ci.siluzan.com/media-report/hour/{entityId}?culture=zh-CN` |
197
+ | TikTok | 日报 | `https://www-ci.siluzan.com/media-report/publish/{entityId}?culture=zh-CN` |
198
198
 
199
199
  `entityId` 来自 `siluzan-tso report list --json-out ./snap` 中每条记录的 `entityId` 字段。
200
200
 
@@ -207,8 +207,8 @@ siluzan-tso report list -m Google --json-out ./snap
207
207
 
208
208
  # 第二步:查看 webUrl
209
209
  siluzan-tso config show
210
- # webUrl: https://www.siluzan.com
210
+ # webUrl: https://www-ci.siluzan.com
211
211
 
212
212
  # 第三步:拼接链接(Google 日报)
213
- # https://www.siluzan.com/media-report/publish/rpt_abc123?culture=zh-CN
213
+ # https://www-ci.siluzan.com/media-report/publish/rpt_abc123?culture=zh-CN
214
214
  ```
@@ -219,7 +219,7 @@
219
219
  ## 十一、常见 HTTP 状态码
220
220
 
221
221
  - **400**:参数错误,查看对应 reference 或 `-h`
222
- - **401**:平台方返回则需用户重新授权;**我方凭据失效**则优先 `send-login-code` + `login --phone --code`,见 `references/core/setup.md`
222
+ - **401**:媒体 OAuth 失效 → `account reauth -m <媒体> --id <entityId>`(须先 delink,见 W9);**丝路赞登录凭据失效** → `send-login-code` + `login --phone --code`,见 `references/core/setup.md`
223
223
  - **500**:服务可能正在部署/升级,建议反馈 Siluzan 相关人员
224
224
 
225
225
  ---
@@ -10,7 +10,7 @@
10
10
  ## 安装 CLI
11
11
 
12
12
  ```bash
13
- npm install -g siluzan-tso-cli
13
+ npm install -g siluzan-tso-cli@beta
14
14
  ```
15
15
 
16
16
  ---
@@ -64,7 +64,7 @@ siluzan-tso config set --api-key <Key> # 或 config 直接写入
64
64
  siluzan-tso config set --token <Token> # 备用:设置 JWT Token
65
65
  ```
66
66
 
67
- API Key 获取入口:`https://www.siluzan.com/v3/foreign_trade/settings/apiKeyManagement`
67
+ API Key 获取入口:`https://www-ci.siluzan.com/v3/foreign_trade/settings/apiKeyManagement`
68
68
 
69
69
  ```bash
70
70
  # 第 1 步:让用户报出手机号后,立刻发码(命令立即返回,不会等待输入)
@@ -129,9 +129,9 @@ siluzan-tso config show
129
129
 
130
130
  ```
131
131
  构建环境 : production
132
- apiBaseUrl : https://tso-api.siluzan.com
133
- googleApiUrl : https://googleapi.mysiluzan.com
134
- webUrl : https://www.siluzan.com
132
+ apiBaseUrl : https://tso-api-ci.siluzan.com
133
+ googleApiUrl : https://googleapi-ci.mysiluzan.com
134
+ webUrl : https://www-ci.siluzan.com
135
135
  apiKey : abcd****1234
136
136
  ```
137
137
 
@@ -152,7 +152,8 @@
152
152
  - **步骤(按场景)**:
153
153
  - **分享**:`list-accounts --json-out` 取 `entityId` → `account share --id <entityId> --phone <手机号>`;查 `account share-detail --customer-id <mediaCustomerId>`;取消 `account unshare --id <entityId> --account-id <userId>`。
154
154
  - **解绑**:`account delink --id <entityId>` / `--ids id1,id2`。
155
- - **OAuth 重授权**:`invalidOAuthToken=true` → `account auth -m <媒体>`(浏览器授权)→ `list-accounts` 验证。
155
+ - **OAuth 重授权**:`invalidOAuthToken=true` → `list-accounts --json-out` 取 `ma.entityId` → **`account reauth -m <媒体> --id <entityId>`**(内置先 delink 再 OAuth,对齐网页「重新授权」)→ `list-accounts` 验证。**禁止**对失效账户直接用 `account auth`(首次「添加授权」专用)。
156
+ - **首次 OAuth 添加授权**:`account auth -m <媒体>`(无需先 delink)。
156
157
  - **MCC**:`account mcc-bind --customers <mediaCustomerId,...> --mcc <MCC客户ID>` / `mcc-unbind`(走 `googleApiUrl`,先 `config show`)。
157
158
  - **BC(TikTok)**:`account bc-bind --customers <id> --bc-ids <id>` / `bc-unbind --bc-id <id>`(解绑一次一个)。
158
159
  - **BM(Meta)**:`account bm-bind --account-id <mediaCustomerId> --bm-id <bmId>`。
@@ -6,7 +6,7 @@
6
6
  siluzan-tso config show # 取 webUrl
7
7
  ```
8
8
 
9
- 首页地址:`https://www.siluzan.com/v3/foreign_trade/tso/home`
9
+ 首页地址:`https://www-ci.siluzan.com/v3/foreign_trade/tso/home`
10
10
 
11
11
  ---
12
12
 
@@ -25,7 +25,7 @@ siluzan-tso config show # 取 webUrl
25
25
 
26
26
  ## 推荐话术
27
27
 
28
- 1. **「和首页一样的总览」** → 打开 `https://www.siluzan.com/v3/foreign_trade/tso/home`。
28
+ 1. **「和首页一样的总览」** → 打开 `https://www-ci.siluzan.com/v3/foreign_trade/tso/home`。
29
29
  2. **「某个 Google 账户昨天花了多少」** → `list-accounts -m Google` + `stats -m Google -a <id>`。
30
30
  3. **「有待充值账户」** → 说明聚合数据在首页;CLI 可 `list-accounts` + `balance` 逐户排查,或引导充值页。
31
31
 
@@ -18,6 +18,7 @@
18
18
  ## 1. 执行摘要(总览)
19
19
 
20
20
  - **CLI**:`siluzan-tso report bing-overview -a <mediaCustomerId> [--start … --end …] --json-out <dir>`
21
+ - **余额**:若 `OverviewSectionData` 误返 `remainingAccountBudget: 0`,CLI 会自动用 `GetMediaAccountInfo` 实时余额回填(与 Web 账户列表一致);**仍建议**单查余额用 `balance -m BingV2`。
21
22
 
22
23
  ## 2. 设备与地域
23
24
 
@@ -18,6 +18,7 @@
18
18
  ## 1. 执行摘要(总览)
19
19
 
20
20
  - **CLI**:`siluzan-tso report bing-overview -a <mediaCustomerId> [--start … --end …] --json-out <dir>`
21
+ - **余额**:若 `OverviewSectionData` 误返 `remainingAccountBudget: 0`,CLI 会自动用 `GetMediaAccountInfo` 实时余额回填(与 Web 账户列表一致);**仍建议**单查余额用 `balance -m BingV2`。
21
22
 
22
23
  ## 2. 设备与地域
23
24
 
@@ -9,11 +9,11 @@ $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.32'
12
+ $PKG_VERSION = '1.1.33-beta.2'
13
13
  $CLI_BIN = 'siluzan-tso'
14
14
  $SKILL_LABEL = 'Siluzan TSO'
15
- $INSTALL_CMD = 'npm install -g siluzan-tso-cli'
16
- $WEB_BASE = 'https://www.siluzan.com'
15
+ $INSTALL_CMD = 'npm install -g siluzan-tso-cli@beta'
16
+ $WEB_BASE = 'https://www-ci.siluzan.com'
17
17
 
18
18
  # -- Constants ----------------------------------------------------------------
19
19
  $NODE_MAJOR_MIN = 18
@@ -9,11 +9,11 @@ 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.32"
12
+ readonly PKG_VERSION="1.1.33-beta.2"
13
13
  readonly CLI_BIN="siluzan-tso"
14
14
  readonly SKILL_LABEL="Siluzan TSO"
15
- readonly INSTALL_CMD="npm install -g siluzan-tso-cli"
16
- readonly WEB_BASE="https://www.siluzan.com"
15
+ readonly INSTALL_CMD="npm install -g siluzan-tso-cli@beta"
16
+ readonly WEB_BASE="https://www-ci.siluzan.com"
17
17
 
18
18
  # -- Constants ----------------------------------------------------------------
19
19
  readonly NODE_MAJOR_MIN=18
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "siluzan-tso-cli",
3
- "version": "1.1.32",
3
+ "version": "1.1.33-beta.2",
4
4
  "description": "Siluzan 广告账户管理 CLI — 查询账户、余额、消耗数据,管理绑定关系与充值。",
5
5
  "keywords": [
6
6
  "ad-account",