siluzan-tso-cli 1.0.0-beta.20 → 1.0.0-beta.22

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
@@ -20,7 +20,7 @@ siluzan-tso init -d /path/to/skills # 写入自定义目录
20
20
  siluzan-tso init --force # 强制覆盖已存在文件
21
21
  ```
22
22
 
23
- > **注意**:当前为测试版(1.0.0-beta.20),供内部测试使用。正式发布后安装命令将改为 `npm install -g siluzan-tso-cli`。
23
+ > **注意**:当前为测试版(1.0.0-beta.22),供内部测试使用。正式发布后安装命令将改为 `npm install -g siluzan-tso-cli`。
24
24
 
25
25
  | 助手 | 建议 `--ai` |
26
26
  |------|-------------|
package/dist/index.js CHANGED
@@ -1,8 +1,8 @@
1
1
  #!/usr/bin/env node
2
2
 
3
3
  // src/index.ts
4
- import * as fs8 from "fs";
5
- import * as path9 from "path";
4
+ import * as fs9 from "fs";
5
+ import * as path10 from "path";
6
6
  import { fileURLToPath as fileURLToPath3 } from "url";
7
7
  import { Command } from "commander";
8
8
 
@@ -183,6 +183,10 @@ function getCurrentVersion(importMetaUrl) {
183
183
  return "0.0.0";
184
184
  }
185
185
  }
186
+ function npmDistTagForCurrentVersion(version) {
187
+ const v = version.trim().replace(/^v/i, "");
188
+ return /^\d+\.\d+\.\d+$/.test(v) ? "latest" : "beta";
189
+ }
186
190
  function isNewer(a, b) {
187
191
  const parse = (v) => {
188
192
  const [base, pre] = v.replace(/^v/, "").split("-beta.");
@@ -485,12 +489,12 @@ async function writeSkillFilesToDir(destDir, skillFiles, force) {
485
489
  return anyWritten;
486
490
  }
487
491
  function saveInstalledTargets(entries) {
488
- const CONFIG_FILE3 = path4.join(os2.homedir(), ".siluzan", "config.json");
492
+ const CONFIG_FILE4 = path4.join(os2.homedir(), ".siluzan", "config.json");
489
493
  try {
490
- fsSync.mkdirSync(path4.dirname(CONFIG_FILE3), { recursive: true });
494
+ fsSync.mkdirSync(path4.dirname(CONFIG_FILE4), { recursive: true });
491
495
  let existing = {};
492
- if (fsSync.existsSync(CONFIG_FILE3)) {
493
- existing = JSON.parse(fsSync.readFileSync(CONFIG_FILE3, "utf8"));
496
+ if (fsSync.existsSync(CONFIG_FILE4)) {
497
+ existing = JSON.parse(fsSync.readFileSync(CONFIG_FILE4, "utf8"));
494
498
  }
495
499
  const prev = Array.isArray(existing.tsoInstalledTargets) ? existing.tsoInstalledTargets : [];
496
500
  const merged = /* @__PURE__ */ new Map();
@@ -498,12 +502,12 @@ function saveInstalledTargets(entries) {
498
502
  merged.set(`${e.target}::${e.cwd}`, e);
499
503
  }
500
504
  fsSync.writeFileSync(
501
- CONFIG_FILE3,
505
+ CONFIG_FILE4,
502
506
  JSON.stringify({ ...existing, tsoInstalledTargets: [...merged.values()] }, null, 2),
503
507
  "utf8"
504
508
  );
505
509
  if (process.platform !== "win32") {
506
- fsSync.chmodSync(CONFIG_FILE3, 384);
510
+ fsSync.chmodSync(CONFIG_FILE4, 384);
507
511
  }
508
512
  } catch {
509
513
  }
@@ -534,9 +538,214 @@ async function runInit(options) {
534
538
  saveInstalledTargets(installedEntries);
535
539
  }
536
540
  console.log("\n\u4E0B\u4E00\u6B65\uFF1A");
537
- console.log("1. \u82E5\u8FD8\u672A\u767B\u5F55\uFF0C\u8BF7\u5148\u8FD0\u884C\uFF1Asiluzan-cso login \uFF08siluzan-tso \u4E0E siluzan-cso \u5171\u7528\u540C\u4E00 Token\uFF09");
538
- console.log("2. OpenClaw \u5168\u5C40\u6280\u80FD\u82E5\u672A\u751F\u6548\uFF0C\u8BF7\u5728 ~/.openclaw/openclaw.json \u7684 skills.load.extraDirs \u4E2D\u52A0\u5165\u6280\u80FD\u7236\u76EE\u5F55\u3002");
539
- console.log("3. WorkBuddy \u6280\u80FD\u5B89\u88C5\u540E\u91CD\u542F WorkBuddy \u5373\u53EF\u751F\u6548\u3002");
541
+ console.log("1. \u82E5\u8FD8\u672A\u767B\u5F55\uFF0C\u8BF7\u5148\u8FD0\u884C\uFF1Asiluzan-tso login \uFF08siluzan-tso \u4E0E siluzan-cso \u5171\u7528\u540C\u4E00 Token\uFF09");
542
+ console.log("2. CLI \u5347\u7EA7\u540E\u5237\u65B0 Skill\uFF1Asiluzan-tso update\uFF08\u6216 siluzan-tso init --ai \u2026 --force\uFF09");
543
+ console.log("3. OpenClaw \u5168\u5C40\u6280\u80FD\u82E5\u672A\u751F\u6548\uFF0C\u8BF7\u5728 ~/.openclaw/openclaw.json \u7684 skills.load.extraDirs \u4E2D\u52A0\u5165\u6280\u80FD\u7236\u76EE\u5F55\u3002");
544
+ console.log("4. \u6216\u8005\u624B\u52A8\u5728siluzan-tso\u7684\u5B89\u88C5\u76EE\u5F55\u4E0B\u6267\u884C\uFF1Anpm install -g siluzan-tso-cli");
545
+ }
546
+
547
+ // src/commands/update.ts
548
+ import * as fs6 from "fs";
549
+ import * as path6 from "path";
550
+ import * as os4 from "os";
551
+ import { spawnSync } from "child_process";
552
+
553
+ // src/utils/version.ts
554
+ import * as fs5 from "fs";
555
+ import * as path5 from "path";
556
+ import * as os3 from "os";
557
+ var PKG_NAME = "siluzan-tso-cli";
558
+ var CONFIG_FILE2 = path5.join(os3.homedir(), ".siluzan", "config.json");
559
+ function getCurrentVersion2() {
560
+ return getCurrentVersion(import.meta.url);
561
+ }
562
+ function readConfigRaw() {
563
+ try {
564
+ return JSON.parse(fs5.readFileSync(CONFIG_FILE2, "utf8"));
565
+ } catch {
566
+ return {};
567
+ }
568
+ }
569
+ function writeConfigRaw(data) {
570
+ try {
571
+ fs5.mkdirSync(path5.dirname(CONFIG_FILE2), { recursive: true });
572
+ fs5.writeFileSync(CONFIG_FILE2, JSON.stringify(data, null, 2), "utf8");
573
+ if (process.platform !== "win32") {
574
+ fs5.chmodSync(CONFIG_FILE2, 384);
575
+ }
576
+ } catch {
577
+ }
578
+ }
579
+ async function fetchVersionByTag(tag, cacheKey, cfg) {
580
+ const hours24 = 24 * 60 * 60 * 1e3;
581
+ if (cfg._tsoLastVersionCheck && cfg[cacheKey]) {
582
+ const lastCheck = new Date(cfg._tsoLastVersionCheck).getTime();
583
+ if (Date.now() - lastCheck < hours24) {
584
+ return cfg[cacheKey];
585
+ }
586
+ }
587
+ return fetchNpmVersion(PKG_NAME, tag);
588
+ }
589
+ async function notifyIfOutdated() {
590
+ try {
591
+ const current = getCurrentVersion2();
592
+ const tag = npmDistTagForCurrentVersion(current);
593
+ const isBeta = tag === "beta";
594
+ const latestCacheKey = isBeta ? "_tsoLatestBeta" : "_tsoLatestStable";
595
+ const minCacheKey = isBeta ? "_tsoMinRequiredBeta" : "_tsoMinRequiredStable";
596
+ const minTag = isBeta ? "min-required-beta" : "min-required";
597
+ const cfg = readConfigRaw();
598
+ const [latest, minRequired] = await Promise.all([
599
+ fetchVersionByTag(tag, latestCacheKey, cfg),
600
+ fetchVersionByTag(minTag, minCacheKey, cfg)
601
+ ]);
602
+ writeConfigRaw({
603
+ ...cfg,
604
+ _tsoLastVersionCheck: (/* @__PURE__ */ new Date()).toISOString(),
605
+ ...latest ? { [latestCacheKey]: latest } : {},
606
+ ...minRequired ? { [minCacheKey]: minRequired } : {}
607
+ });
608
+ const lastNotified = cfg._tsoLastNotified ? new Date(cfg._tsoLastNotified).getTime() : 0;
609
+ if (Date.now() - lastNotified < 24 * 60 * 60 * 1e3) return;
610
+ const tagLabel = isBeta ? "\uFF08\u6D4B\u8BD5\u7248\uFF09" : "\uFF08\u6B63\u5F0F\u7248\uFF09";
611
+ if (minRequired && isNewer(current, minRequired)) {
612
+ writeConfigRaw({ ...cfg, _tsoLastNotified: (/* @__PURE__ */ new Date()).toISOString() });
613
+ console.log(
614
+ `
615
+ [SILUZAN_FORCE_UPDATE] \u5F53\u524D\u7248\u672C ${current} \u5DF2\u8FC7\u671F\uFF0C\u6700\u4F4E\u8981\u6C42\u7248\u672C ${minRequired}${tagLabel}
616
+ \u26D4 \u5FC5\u987B\u5148\u5347\u7EA7\u624D\u80FD\u7EE7\u7EED\u4F7F\u7528\uFF0CAgent \u8BF7\u7ACB\u5373\u6267\u884C\uFF1Asiluzan-tso update
617
+ \u6216\u624B\u52A8\uFF1Anpm install -g ${PKG_NAME}@${tag}
618
+ `
619
+ );
620
+ return;
621
+ }
622
+ if (latest && isNewer(current, latest)) {
623
+ writeConfigRaw({ ...cfg, _tsoLastNotified: (/* @__PURE__ */ new Date()).toISOString() });
624
+ console.log(
625
+ `
626
+ [SILUZAN_UPDATE_AVAILABLE] \u53D1\u73B0\u65B0\u7248\u672C ${latest}${tagLabel}\uFF08\u5F53\u524D ${current}\uFF09
627
+ \u{1F4A1} Agent \u8BF7\u7ACB\u5373\u6267\u884C\uFF1Asiluzan-tso update
628
+ \u6216\u624B\u52A8\uFF1Anpm install -g ${PKG_NAME}@${tag}
629
+ `
630
+ );
631
+ }
632
+ } catch {
633
+ }
634
+ }
635
+ async function fetchLatestVersion() {
636
+ const tag = npmDistTagForCurrentVersion(getCurrentVersion2());
637
+ return fetchNpmVersion(PKG_NAME, tag);
638
+ }
639
+
640
+ // src/commands/update.ts
641
+ var CONFIG_FILE3 = path6.join(os4.homedir(), ".siluzan", "config.json");
642
+ var PKG_NAME2 = "siluzan-tso-cli";
643
+ function readInstalledTargets() {
644
+ try {
645
+ const cfg = JSON.parse(fs6.readFileSync(CONFIG_FILE3, "utf8"));
646
+ return Array.isArray(cfg.tsoInstalledTargets) ? cfg.tsoInstalledTargets : [];
647
+ } catch {
648
+ return [];
649
+ }
650
+ }
651
+ function isNpmGlobalInstall() {
652
+ try {
653
+ const result = spawnSync("npm", ["list", "-g", "--depth=0", PKG_NAME2], {
654
+ encoding: "utf8",
655
+ stdio: "pipe"
656
+ });
657
+ return Boolean(result.stdout?.includes(PKG_NAME2));
658
+ } catch {
659
+ return false;
660
+ }
661
+ }
662
+ async function runUpdate(options) {
663
+ const current = getCurrentVersion2();
664
+ console.log(`
665
+ \u5F53\u524D\u7248\u672C\uFF1A${current}`);
666
+ console.log("\u6B63\u5728\u67E5\u8BE2 npm registry \u2026");
667
+ const latest = await fetchLatestVersion();
668
+ if (!latest) {
669
+ console.warn("\u26A0\uFE0F \u65E0\u6CD5\u8BBF\u95EE npm registry\uFF0C\u8BF7\u68C0\u67E5\u7F51\u7EDC\u540E\u91CD\u8BD5\u3002");
670
+ console.warn(" \u82E5\u9700\u5F3A\u5236\u91CD\u65B0\u5B89\u88C5\u5F53\u524D\u7248\u672C\uFF0C\u52A0 --force \u53C2\u6570\u3002");
671
+ if (!options.force) {
672
+ process.exit(1);
673
+ }
674
+ } else {
675
+ console.log(`\u6700\u65B0\u7248\u672C\uFF1A${latest}`);
676
+ }
677
+ const shouldUpdate = options.force || (latest ? isNewer(current, latest) : false);
678
+ if (!shouldUpdate && latest) {
679
+ console.log("\n\u2705 \u5DF2\u662F\u6700\u65B0\u7248\u672C\uFF0C\u65E0\u9700\u66F4\u65B0\u3002");
680
+ console.log(" \u5982\u9700\u4EC5\u5237\u65B0 Skill \u6587\u4EF6\uFF0C\u53EF\u52A0 --force \u53C2\u6570\u3002");
681
+ return;
682
+ }
683
+ const targetVersion = latest ?? current;
684
+ if (shouldUpdate && latest) {
685
+ console.log(`
686
+ \u2B06\uFE0F \u6B63\u5728\u66F4\u65B0 CLI \u81F3 ${targetVersion} \u2026`);
687
+ } else if (options.force) {
688
+ console.log(`
689
+ \u{1F504} \u5F3A\u5236\u91CD\u65B0\u5B89\u88C5\u5F53\u524D\u7248\u672C ${current} \u2026`);
690
+ }
691
+ if (isNpmGlobalInstall()) {
692
+ try {
693
+ const result = spawnSync(
694
+ "npm",
695
+ ["install", "-g", `${PKG_NAME2}@${targetVersion}`],
696
+ { stdio: "inherit", shell: false }
697
+ );
698
+ if (result.status !== 0) {
699
+ throw new Error(`npm install \u9000\u51FA\u7801\uFF1A${result.status ?? "unknown"}`);
700
+ }
701
+ console.log("\n\u2705 CLI \u66F4\u65B0\u6210\u529F\uFF01");
702
+ } catch (e) {
703
+ console.error(`
704
+ \u274C npm \u5B89\u88C5\u5931\u8D25\uFF1A${e.message}`);
705
+ console.error(" \u5982\u679C\u662F\u6743\u9650\u95EE\u9898\uFF0C\u5C1D\u8BD5\u52A0 sudo\uFF08Unix\uFF09\u6216\u4EE5\u7BA1\u7406\u5458\u8EAB\u4EFD\u8FD0\u884C\u3002");
706
+ process.exit(1);
707
+ }
708
+ } else {
709
+ console.log(
710
+ "\n\u26A0\uFE0F \u68C0\u6D4B\u5230 CLI \u5E76\u975E\u901A\u8FC7 npm \u5168\u5C40\u5B89\u88C5\uFF08\u53EF\u80FD\u662F\u672C\u5730\u5F00\u53D1\u6A21\u5F0F\uFF09\u3002\n \u8DF3\u8FC7 npm \u66F4\u65B0\uFF0C\u4EC5\u91CD\u65B0\u521D\u59CB\u5316 Skill \u6587\u4EF6\u3002\n \u82E5\u9700\u6B63\u5F0F\u5B89\u88C5\uFF0C\u8BF7\u8FD0\u884C\uFF1Anpm install -g siluzan-tso-cli"
711
+ );
712
+ }
713
+ if (options.skipInit) return;
714
+ const targets = readInstalledTargets();
715
+ if (targets.length === 0) {
716
+ console.log(
717
+ "\n\u26A0\uFE0F \u672A\u627E\u5230\u4E0A\u6B21\u5B89\u88C5\u8BB0\u5F55\uFF08tsoInstalledTargets\uFF09\u3002\n \u8BF7\u624B\u52A8\u8FD0\u884C\uFF1Asiluzan-tso init --ai <\u5E73\u53F0> --force\n \u4EE5\u5237\u65B0 Skill \u6587\u4EF6\u3002"
718
+ );
719
+ return;
720
+ }
721
+ console.log(`
722
+ \u{1F504} \u6B63\u5728\u66F4\u65B0 ${targets.length} \u5904 Skill \u5B89\u88C5\u4F4D\u7F6E \u2026`);
723
+ for (const entry of targets) {
724
+ const label = entry.target === "custom" ? entry.dir ?? "unknown" : entry.target;
725
+ const cwd = entry.cwd || os4.homedir();
726
+ console.log(`
727
+ [${label}]`);
728
+ try {
729
+ if (entry.target === "custom" && entry.dir) {
730
+ await runInit({
731
+ cwd: os4.homedir(),
732
+ aiTargets: "",
733
+ dir: entry.dir,
734
+ force: true
735
+ });
736
+ } else {
737
+ await runInit({
738
+ cwd,
739
+ aiTargets: entry.target,
740
+ force: true
741
+ });
742
+ }
743
+ } catch (e) {
744
+ console.error(` \u274C \u66F4\u65B0\u5931\u8D25\uFF1A${e.message}`);
745
+ }
746
+ }
747
+ console.log("\n\u2705 \u5168\u90E8 Skill \u6587\u4EF6\u5DF2\u5237\u65B0\u3002");
748
+ console.log(" \u5982\u679C AI \u52A9\u624B\u6B63\u5728\u8FD0\u884C\uFF0C\u5EFA\u8BAE\u91CD\u542F\u4EE5\u4F7F\u65B0 Skill \u6587\u4EF6\u751F\u6548\u3002\n");
540
749
  }
541
750
 
542
751
  // src/commands/list-accounts.ts
@@ -1446,19 +1655,19 @@ async function runTransferList(opts) {
1446
1655
  }
1447
1656
 
1448
1657
  // src/commands/invoice.ts
1449
- import * as fs5 from "fs";
1450
- import * as path5 from "path";
1451
- import * as os3 from "os";
1658
+ import * as fs7 from "fs";
1659
+ import * as path7 from "path";
1660
+ import * as os5 from "os";
1452
1661
  async function ensureDataPermission(config) {
1453
1662
  if (config.dataPermission) return config;
1454
1663
  if (!config.mainApiUrl) return config;
1455
1664
  const dp = await fetchDataPermission(config.mainApiUrl, config.authToken);
1456
1665
  if (!dp) return config;
1457
- const configPath = path5.join(os3.homedir(), ".siluzan", "config.json");
1666
+ const configPath = path7.join(os5.homedir(), ".siluzan", "config.json");
1458
1667
  try {
1459
- const raw = fs5.existsSync(configPath) ? JSON.parse(fs5.readFileSync(configPath, "utf8")) : {};
1668
+ const raw = fs7.existsSync(configPath) ? JSON.parse(fs7.readFileSync(configPath, "utf8")) : {};
1460
1669
  raw.dataPermission = dp;
1461
- fs5.writeFileSync(configPath, JSON.stringify(raw, null, 2), "utf8");
1670
+ fs7.writeFileSync(configPath, JSON.stringify(raw, null, 2), "utf8");
1462
1671
  } catch {
1463
1672
  }
1464
1673
  return { ...config, dataPermission: dp };
@@ -3165,8 +3374,8 @@ async function runOptimizeChildren(opts) {
3165
3374
  }
3166
3375
 
3167
3376
  // src/commands/forewarning.ts
3168
- import os4 from "os";
3169
- import path6 from "path";
3377
+ import os6 from "os";
3378
+ import path8 from "path";
3170
3379
  import QRCode from "qrcode";
3171
3380
  import open from "open";
3172
3381
  var VALID_MEDIA_TYPES7 = ["Google", "TikTok"];
@@ -3373,7 +3582,7 @@ async function runForewarningNotifyAccounts(opts) {
3373
3582
  console.log(" \u901A\u77E5\u6E20\u9053\uFF1A\u4E1D\u8DEF\u8D5E\u5E73\u53F0\u5FAE\u4FE1\u670D\u52A1\u53F7\uFF08\u9700\u626B\u7801\u5173\u6CE8\u540E\u624D\u80FD\u6536\u5230\u9884\u8B66\u901A\u77E5\uFF09\n");
3374
3583
  if (qrLink) {
3375
3584
  try {
3376
- const imgPath = path6.join(os4.tmpdir(), "siluzan-wechat-qr.png");
3585
+ const imgPath = path8.join(os6.tmpdir(), "siluzan-wechat-qr.png");
3377
3586
  await QRCode.toFile(imgPath, qrLink, { width: 300 });
3378
3587
  await open(imgPath);
3379
3588
  console.log(` \u{1F4F7} \u4E8C\u7EF4\u7801\u5DF2\u5728\u6D4F\u89C8\u5668\u4E2D\u6253\u5F00\uFF0C\u8BF7\u7528\u624B\u673A\u5FAE\u4FE1\u626B\u7801\u5173\u6CE8"\u4E1D\u8DEF\u8D5E\u5E73\u53F0"\u670D\u52A1\u53F7
@@ -5972,8 +6181,8 @@ async function runAccountBmBind(opts) {
5972
6181
  }
5973
6182
 
5974
6183
  // src/commands/open-account.ts
5975
- import * as fs6 from "fs";
5976
- import * as path7 from "path";
6184
+ import * as fs8 from "fs";
6185
+ import * as path9 from "path";
5977
6186
  import * as readline from "readline/promises";
5978
6187
  import { stdin as stdinStream, stdout as stdoutStream } from "process";
5979
6188
  async function runListAdvertiserGroups(opts) {
@@ -6101,8 +6310,6 @@ async function resolveGoogleMediaAccountGroupId(config, opts) {
6101
6310
  }
6102
6311
  const company = opts.company.trim();
6103
6312
  const promotionLink = normalizePromotionLinkForGoogle(opts.promotionLink);
6104
- const level1 = (opts.industryLevel1 ?? "").trim();
6105
- const level2 = (opts.industryLevel2 ?? "").trim();
6106
6313
  let profile;
6107
6314
  try {
6108
6315
  profile = await apiFetch2(
@@ -6125,7 +6332,6 @@ async function resolveGoogleMediaAccountGroupId(config, opts) {
6125
6332
  const mediaAccountGroupPayload = {
6126
6333
  advertiserName: company,
6127
6334
  StorageProvider: storageFromGroup ?? "StorageAccount",
6128
- industry: { level1, level2 },
6129
6335
  promotionLink,
6130
6336
  promotionType: opts.promotionType
6131
6337
  };
@@ -6222,8 +6428,8 @@ async function runOpenAccountYandex(opts) {
6222
6428
  }
6223
6429
  }
6224
6430
  async function uploadAttachment(filePath, apiBaseUrl, config, verbose) {
6225
- const fileName = path7.basename(filePath);
6226
- const fileBuffer = fs6.readFileSync(filePath);
6431
+ const fileName = path9.basename(filePath);
6432
+ const fileBuffer = fs8.readFileSync(filePath);
6227
6433
  const mimeType = guessContentType(fileName);
6228
6434
  const form = new FormData();
6229
6435
  form.append("file", new Blob([fileBuffer], { type: mimeType }), fileName);
@@ -6249,7 +6455,7 @@ async function uploadAttachment(filePath, apiBaseUrl, config, verbose) {
6249
6455
  return { id: data.id, fileName };
6250
6456
  }
6251
6457
  function guessContentType(fileName) {
6252
- const ext = path7.extname(fileName).toLowerCase();
6458
+ const ext = path9.extname(fileName).toLowerCase();
6253
6459
  const map = {
6254
6460
  ".jpg": "image/jpeg",
6255
6461
  ".jpeg": "image/jpeg",
@@ -6262,7 +6468,7 @@ function guessContentType(fileName) {
6262
6468
  }
6263
6469
  async function runOpenAccountBing(opts) {
6264
6470
  const config = loadConfig(opts.token);
6265
- if (!fs6.existsSync(opts.licenseFile)) {
6471
+ if (!fs8.existsSync(opts.licenseFile)) {
6266
6472
  console.error(`
6267
6473
  \u274C \u8425\u4E1A\u6267\u7167\u6587\u4EF6\u4E0D\u5B58\u5728\uFF1A${opts.licenseFile}
6268
6474
  `);
@@ -6289,9 +6495,10 @@ async function runOpenAccountBing(opts) {
6289
6495
  opts.verbose,
6290
6496
  opts.advertiserId
6291
6497
  );
6498
+ const accountCount = Math.min(Math.max(opts.accountCount ?? 1, 1), 6);
6292
6499
  const body = {
6293
6500
  MediaAccountGroupId: magKey,
6294
- AccountCount: opts.accountCount ?? 1,
6501
+ AccountCount: accountCount,
6295
6502
  BingCustomerInfo: {
6296
6503
  name: opts.advertiserName,
6297
6504
  province: opts.province,
@@ -6337,9 +6544,9 @@ async function runOpenAccountBing(opts) {
6337
6544
  }
6338
6545
  }
6339
6546
  async function uploadToKwai(filePath, apiBaseUrl, config, verbose) {
6340
- const ext = path7.extname(filePath).toLowerCase().replace(".", "").replace("jpg", "jpeg");
6547
+ const ext = path9.extname(filePath).toLowerCase().replace(".", "").replace("jpg", "jpeg");
6341
6548
  const imageType = ext || "jpeg";
6342
- const fileBuffer = fs6.readFileSync(filePath);
6549
+ const fileBuffer = fs8.readFileSync(filePath);
6343
6550
  const base64Image = fileBuffer.toString("base64");
6344
6551
  const res = await apiFetch2(
6345
6552
  `${apiBaseUrl}/KwaiAccount/Management/Upload`,
@@ -6358,7 +6565,7 @@ async function uploadToKwai(filePath, apiBaseUrl, config, verbose) {
6358
6565
  }
6359
6566
  async function runOpenAccountKwai(opts) {
6360
6567
  const config = loadConfig(opts.token);
6361
- if (!fs6.existsSync(opts.licenseFile)) {
6568
+ if (!fs8.existsSync(opts.licenseFile)) {
6362
6569
  console.error(`
6363
6570
  \u274C \u8425\u4E1A\u6267\u7167\u6587\u4EF6\u4E0D\u5B58\u5728\uFF1A${opts.licenseFile}
6364
6571
  `);
@@ -6618,18 +6825,14 @@ async function runOpenAccountGoogle(opts) {
6618
6825
  company: opts.company,
6619
6826
  promotionLink: opts.promotionLink,
6620
6827
  promotionType: opts.promotionType,
6621
- industryLevel1: opts.industryLevel1,
6622
- industryLevel2: opts.industryLevel2,
6623
6828
  verbose: opts.verbose
6624
6829
  });
6625
- const level1 = (opts.industryLevel1 ?? "").trim();
6626
- const level2 = (opts.industryLevel2 ?? "").trim();
6627
- const industryStr = level1 || level2 ? `${level1}-${level2}` : "";
6628
6830
  const promotionLinkNorm = normalizePromotionLinkForGoogle(opts.promotionLink);
6629
6831
  const body = {
6630
- accountInfo: Array.from({ length: counts }, () => ({
6832
+ accountInfo: Array.from({ length: counts }, (_, i) => ({
6631
6833
  advertiser_info: {
6632
- name: opts.accountName,
6834
+ // 多账户时在名称末尾追加 -1, -2 … 以便区分
6835
+ name: counts > 1 ? `${opts.accountName}-${i + 1}` : opts.accountName,
6633
6836
  currency: opts.currency,
6634
6837
  timezone: opts.timezone,
6635
6838
  accounttype: "Adwords",
@@ -6637,8 +6840,7 @@ async function runOpenAccountGoogle(opts) {
6637
6840
  inviteduserrole: opts.inviteRole ?? "Standard"
6638
6841
  },
6639
6842
  customer_info: {
6640
- company: opts.company.trim(),
6641
- industry: industryStr
6843
+ company: opts.company.trim()
6642
6844
  },
6643
6845
  qualification_info: {
6644
6846
  promotion_link: promotionLinkNorm,
@@ -6674,8 +6876,8 @@ async function runOpenAccountGoogle(opts) {
6674
6876
  }
6675
6877
  }
6676
6878
  async function uploadLicenseToTikTok(filePath, businessCentreType, config, verbose) {
6677
- const fileName = path7.basename(filePath);
6678
- const fileBuffer = fs6.readFileSync(filePath);
6879
+ const fileName = path9.basename(filePath);
6880
+ const fileBuffer = fs8.readFileSync(filePath);
6679
6881
  const base64Image = fileBuffer.toString("base64");
6680
6882
  const res = await apiFetch2(
6681
6883
  `${config.apiBaseUrl}/command/media-account/tiktok/Upload`,
@@ -6697,34 +6899,16 @@ async function uploadLicenseToTikTok(filePath, businessCentreType, config, verbo
6697
6899
  }
6698
6900
  return imageId;
6699
6901
  }
6700
- async function checkUnionpayRequired(licenseNo, company, businessCentreType, config, verbose) {
6701
- try {
6702
- const params = new URLSearchParams({
6703
- license_no: licenseNo,
6704
- company_name: company,
6705
- businessCentreType
6706
- });
6707
- const res = await apiFetch2(
6708
- `${config.apiBaseUrl}/query/media-account/tiktok/TikTokAdvQuery/CheckUnionpayInfo?${params}`,
6709
- config,
6710
- {},
6711
- verbose
6712
- );
6713
- return res?.data?.unionpay_verification_required === true;
6714
- } catch {
6715
- return false;
6716
- }
6717
- }
6718
6902
  async function runOpenAccountTikTok(opts) {
6719
6903
  const config = loadConfig(opts.token);
6720
6904
  const bcType = opts.businessCentreType ?? "Shop";
6721
- if (!fs6.existsSync(opts.licenseFile)) {
6905
+ if (!fs8.existsSync(opts.licenseFile)) {
6722
6906
  console.error(`
6723
6907
  \u274C \u8425\u4E1A\u6267\u7167\u6587\u4EF6\u4E0D\u5B58\u5728\uFF1A${opts.licenseFile}
6724
6908
  `);
6725
6909
  process.exit(1);
6726
6910
  }
6727
- console.log(" [1/4] \u6B63\u5728\u4E0A\u4F20\u8425\u4E1A\u6267\u7167\u5230 TikTok...");
6911
+ console.log(" [1/3] \u6B63\u5728\u4E0A\u4F20\u8425\u4E1A\u6267\u7167\u5230 TikTok...");
6728
6912
  let licenseImageId;
6729
6913
  try {
6730
6914
  licenseImageId = await uploadLicenseToTikTok(opts.licenseFile, bcType, config, opts.verbose);
@@ -6735,7 +6919,7 @@ async function runOpenAccountTikTok(opts) {
6735
6919
  process.exit(1);
6736
6920
  }
6737
6921
  console.log(` TikTok license_image_id: ${licenseImageId}`);
6738
- console.log(" [2/4] \u6B63\u5728\u4E0A\u4F20\u8425\u4E1A\u6267\u7167\u5230 Siluzan \u5B58\u6863...");
6922
+ console.log(" [2/3] \u6B63\u5728\u4E0A\u4F20\u8425\u4E1A\u6267\u7167\u5230 Siluzan \u5B58\u6863...");
6739
6923
  let siluzanImageId = "";
6740
6924
  try {
6741
6925
  const uploaded = await uploadAttachment(opts.licenseFile, config.apiBaseUrl, config, opts.verbose);
@@ -6744,7 +6928,7 @@ async function runOpenAccountTikTok(opts) {
6744
6928
  } catch (err) {
6745
6929
  console.warn(` \u26A0\uFE0F Siluzan \u5B58\u6863\u4E0A\u4F20\u5931\u8D25\uFF08\u4E0D\u5F71\u54CD\u5F00\u6237\uFF09\uFF1A${err instanceof Error ? err.message : String(err)}`);
6746
6930
  }
6747
- console.log(" [3/4] \u6B63\u5728\u5173\u8054\u5E7F\u544A\u4E3B\u7EC4...");
6931
+ console.log(" [3/3] \u6B63\u5728\u5173\u8054\u5E7F\u544A\u4E3B\u7EC4...");
6748
6932
  const magKey = await resolveGenericMagKey(
6749
6933
  config,
6750
6934
  opts.company,
@@ -6760,19 +6944,11 @@ async function runOpenAccountTikTok(opts) {
6760
6944
  opts.verbose,
6761
6945
  opts.advertiserId
6762
6946
  );
6763
- console.log(" [4/4] \u6B63\u5728\u68C0\u67E5\u6CD5\u4EBA\u9A8C\u8BC1\u8981\u6C42...");
6764
- const needUnionpay = await checkUnionpayRequired(opts.licenseNo, opts.company, bcType, config, opts.verbose);
6765
- if (needUnionpay && (!opts.representativeName || !opts.representativeId)) {
6766
- console.error(
6767
- "\n\u274C \u8BE5\u8425\u4E1A\u6267\u7167\u9700\u8981\u586B\u5199\u6CD5\u4EBA\u94F6\u8054\u9A8C\u8BC1\u4FE1\u606F\uFF0C\u8BF7\u8FFD\u52A0\u4EE5\u4E0B\u53C2\u6570\u518D\u91CD\u8BD5\uFF1A\n --representative-name <\u6CD5\u4EBA\u59D3\u540D>\n --representative-id <\u8EAB\u4EFD\u8BC1\u53F7>\n --unionpay-account <\u94F6\u8054\u8D26\u53F7>\n --representative-phone <\u624B\u673A\u53F7>\n"
6768
- );
6769
- process.exit(1);
6770
- }
6771
6947
  const counts = Math.min(Math.max(opts.counts ?? 1, 1), 10);
6772
6948
  const singleAccountInfo = {
6773
6949
  advertiser_info: {
6774
6950
  name: opts.accountName,
6775
- currency: opts.currency,
6951
+ currency: "USD",
6776
6952
  timezone: opts.timezone
6777
6953
  },
6778
6954
  customer_info: {
@@ -6784,18 +6960,23 @@ async function runOpenAccountTikTok(opts) {
6784
6960
  promotion_link: opts.promotionLink,
6785
6961
  license_no: opts.licenseNo,
6786
6962
  license_image_id: licenseImageId
6963
+ },
6964
+ representative_info: {
6965
+ representative_name: opts.representativeName,
6966
+ representative_id: opts.representativeId,
6967
+ unionpay_account: opts.unionpayAccount,
6968
+ representative_phone_number: opts.representativePhone
6787
6969
  }
6788
6970
  };
6789
- if (needUnionpay && opts.representativeName) {
6790
- singleAccountInfo.representative_info = {
6791
- representative_name: opts.representativeName,
6792
- representative_id: opts.representativeId ?? "",
6793
- unionpay_account: opts.unionpayAccount ?? "",
6794
- representative_phone_number: opts.representativePhone ?? ""
6795
- };
6796
- }
6797
6971
  const body = {
6798
- accountInfo: Array.from({ length: counts }, () => ({ ...singleAccountInfo })),
6972
+ accountInfo: Array.from({ length: counts }, (_, i) => ({
6973
+ ...singleAccountInfo,
6974
+ advertiser_info: {
6975
+ ...singleAccountInfo.advertiser_info,
6976
+ // 多账户时在名称末尾追加 -1, -2 … 以便区分
6977
+ name: counts > 1 ? `${opts.accountName}-${i + 1}` : opts.accountName
6978
+ }
6979
+ })),
6799
6980
  MediaAccountGroupId: magKey,
6800
6981
  IsSaltAdd: false,
6801
6982
  ManagerCustomerId: "",
@@ -7080,79 +7261,12 @@ async function runLogin(opts = {}) {
7080
7261
  console.log(" siluzan-tso balance -m Google \u67E5\u770B\u8D26\u6237\u4F59\u989D\n");
7081
7262
  }
7082
7263
 
7083
- // src/utils/version.ts
7084
- import * as fs7 from "fs";
7085
- import * as path8 from "path";
7086
- import * as os5 from "os";
7087
- var PKG_NAME = "siluzan-tso-cli";
7088
- var CONFIG_FILE2 = path8.join(os5.homedir(), ".siluzan", "config.json");
7089
- function getCurrentVersion2() {
7090
- return getCurrentVersion(import.meta.url);
7091
- }
7092
- function isBetaVersion(version) {
7093
- return version.includes("-beta");
7094
- }
7095
- function readConfigRaw() {
7096
- try {
7097
- return JSON.parse(fs7.readFileSync(CONFIG_FILE2, "utf8"));
7098
- } catch {
7099
- return {};
7100
- }
7101
- }
7102
- function writeConfigRaw(data) {
7103
- try {
7104
- fs7.mkdirSync(path8.dirname(CONFIG_FILE2), { recursive: true });
7105
- fs7.writeFileSync(CONFIG_FILE2, JSON.stringify(data, null, 2), "utf8");
7106
- if (process.platform !== "win32") {
7107
- fs7.chmodSync(CONFIG_FILE2, 384);
7108
- }
7109
- } catch {
7110
- }
7111
- }
7112
- async function fetchVersionByTag(tag, cfg) {
7113
- const cacheKey = tag === "beta" ? "_tsoLatestBeta" : "_tsoLatestStable";
7114
- const hours24 = 24 * 60 * 60 * 1e3;
7115
- if (cfg._tsoLastVersionCheck && cfg[cacheKey]) {
7116
- const lastCheck = new Date(cfg._tsoLastVersionCheck).getTime();
7117
- if (Date.now() - lastCheck < hours24) {
7118
- return cfg[cacheKey];
7119
- }
7120
- }
7121
- return fetchNpmVersion(PKG_NAME, tag);
7122
- }
7123
- async function notifyIfOutdated() {
7124
- try {
7125
- const current = getCurrentVersion2();
7126
- const isBeta = isBetaVersion(current);
7127
- const tag = isBeta ? "beta" : "latest";
7128
- const cfg = readConfigRaw();
7129
- const latest = await fetchVersionByTag(tag, cfg);
7130
- if (!latest) return;
7131
- const cacheKey = isBeta ? "_tsoLatestBeta" : "_tsoLatestStable";
7132
- writeConfigRaw({
7133
- ...cfg,
7134
- _tsoLastVersionCheck: (/* @__PURE__ */ new Date()).toISOString(),
7135
- [cacheKey]: latest
7136
- });
7137
- if (isNewer(current, latest)) {
7138
- const tagLabel = isBeta ? "\uFF08\u6D4B\u8BD5\u7248\uFF09" : "\uFF08\u6B63\u5F0F\u7248\uFF09";
7139
- console.error(
7140
- `
7141
- \u{1F4A1} \u53D1\u73B0\u65B0\u7248\u672C ${latest}${tagLabel}\uFF08\u5F53\u524D ${current}\uFF09\uFF0C\u8FD0\u884C\u4EE5\u4E0B\u547D\u4EE4\u5347\u7EA7\uFF1A
7142
- npm install -g ${PKG_NAME}@${tag}
7143
- `
7144
- );
7145
- }
7146
- } catch {
7147
- }
7148
- }
7149
-
7150
7264
  // src/index.ts
7151
7265
  function getVersion() {
7152
7266
  try {
7153
- const __dirname2 = path9.dirname(fileURLToPath3(import.meta.url));
7154
- const pkgPath = path9.join(__dirname2, "..", "package.json");
7155
- const pkg = JSON.parse(fs8.readFileSync(pkgPath, "utf8"));
7267
+ const __dirname2 = path10.dirname(fileURLToPath3(import.meta.url));
7268
+ const pkgPath = path10.join(__dirname2, "..", "package.json");
7269
+ const pkg = JSON.parse(fs9.readFileSync(pkgPath, "utf8"));
7156
7270
  return pkg.version ?? "0.0.0";
7157
7271
  } catch {
7158
7272
  return "0.0.0";
@@ -7177,6 +7291,9 @@ configCmd.command("set").description("\u4FDD\u5B58\u914D\u7F6E\u5230 ~/.siluzan/
7177
7291
  cmdConfigSet({ apiKey: opts.apiKey, token: opts.token, apiBase: opts.apiBase, googleApi: opts.googleApi });
7178
7292
  });
7179
7293
  configCmd.command("clear").description("\u6E05\u7A7A\u5DF2\u4FDD\u5B58\u7684 Token").action(() => cmdConfigClear());
7294
+ program.command("update").description("\u68C0\u67E5\u5E76\u66F4\u65B0 siluzan-tso-cli \u81F3\u6700\u65B0\u7248\u672C\uFF0C\u540C\u6B65\u5237\u65B0\u6240\u6709\u5DF2\u5B89\u88C5\u7684 Skill \u6587\u4EF6").option("--force", "\u8DF3\u8FC7\u7248\u672C\u6BD4\u8F83\uFF0C\u5F3A\u5236\u91CD\u65B0\u5B89\u88C5\u5E76\u5237\u65B0 Skill \u6587\u4EF6", false).option("--skip-init", "\u4EC5\u66F4\u65B0 CLI\uFF0C\u4E0D\u91CD\u65B0\u521D\u59CB\u5316 Skill \u6587\u4EF6", false).action(async (opts) => {
7295
+ await runUpdate({ force: opts.force, skipInit: opts.skipInit });
7296
+ });
7180
7297
  program.command("init").description("\u5C06 siluzan-tso Skill \u6587\u4EF6\u5199\u5165\u6307\u5B9A AI \u52A9\u624B\u76EE\u5F55").option(
7181
7298
  "-a, --ai <targets>",
7182
7299
  "\u76EE\u6807\u5E73\u53F0\uFF0C\u9017\u53F7\u5206\u9694\uFF1Acursor,claude,openclaw-workspace,openclaw-global,workbuddy-workspace,workbuddy-global,all",
@@ -8369,7 +8486,7 @@ openAccountCmd.command("yandex").description("\u63D0\u4EA4 Yandex \u5F00\u6237\u
8369
8486
  verbose: opts.verbose
8370
8487
  });
8371
8488
  });
8372
- openAccountCmd.command("bing").description("\u63D0\u4EA4 Bing/BingV2 \u5F00\u6237\u7533\u8BF7\uFF08\u9700\u4E0A\u4F20\u8425\u4E1A\u6267\u7167\u56FE\u7247\uFF0C\u6309\u516C\u53F8\u540D\u81EA\u52A8\u521B\u5EFA/\u5173\u8054\u5E7F\u544A\u4E3B\u7EC4\uFF09").requiredOption("--advertiser-name <name>", "\u5E7F\u544A\u4E3B\u540D\u79F0\uFF08\u516C\u53F8\u5168\u79F0\uFF0C\u7528\u4E8E\u5339\u914D\u6216\u521B\u5EFA\u5E7F\u544A\u4E3B\u7EC4\uFF09").requiredOption("--name-short <short>", "\u5E7F\u544A\u4E3B\u7B80\u79F0").requiredOption("--province <province>", "\u7701/\u5DDE").requiredOption("--city <city>", "\u57CE\u5E02").requiredOption("--address <address>", "\u8BE6\u7EC6\u5730\u5740").requiredOption("--promotion-link <url>", "\u63A8\u5E7F\u94FE\u63A5").requiredOption("--trade-id <name>", "\u884C\u4E1A\u540D\u79F0\uFF08\u6765\u81EA open-account bing-industries \u7684 name \u5B57\u6BB5\uFF09").requiredOption("--license-file <path>", "\u8425\u4E1A\u6267\u7167\u56FE\u7247\u672C\u5730\u8DEF\u5F84\uFF08JPG/PNG/PDF\uFF09").option("--postcode <code>", "\u90AE\u653F\u7F16\u7801").option("--advertiser-cid <cid>", "\u7ECF\u7406\u8D26\u6237 CID").option("--advertiser-name2 <name>", "\u7ECF\u7406\u8D26\u6237\u540D\u79F0").option("--account-count <n>", "\u5F00\u6237\u6570\u91CF\uFF08\u9ED8\u8BA4 1\uFF09", parseInt).option("--advertiser-id <magKey>", "\u53EF\u9009\uFF1A\u624B\u52A8\u6307\u5B9A\u5E7F\u544A\u4E3B\u7EC4 magKey\uFF08\u4E00\u822C\u65E0\u9700\u586B\u5199\uFF09").option("-t, --token <token>", "Auth Token").option("--verbose", "\u8BE6\u7EC6\u9519\u8BEF\u4FE1\u606F", false).action(async (opts) => {
8489
+ openAccountCmd.command("bing").description("\u63D0\u4EA4 Bing/BingV2 \u5F00\u6237\u7533\u8BF7\uFF08\u9700\u4E0A\u4F20\u8425\u4E1A\u6267\u7167\u56FE\u7247\uFF0C\u6309\u516C\u53F8\u540D\u81EA\u52A8\u521B\u5EFA/\u5173\u8054\u5E7F\u544A\u4E3B\u7EC4\uFF09").requiredOption("--advertiser-name <name>", "\u5E7F\u544A\u4E3B\u540D\u79F0\uFF08\u516C\u53F8\u5168\u79F0\uFF0C\u7528\u4E8E\u5339\u914D\u6216\u521B\u5EFA\u5E7F\u544A\u4E3B\u7EC4\uFF09").requiredOption("--name-short <short>", "\u5E7F\u544A\u4E3B\u7B80\u79F0").requiredOption("--province <province>", "\u7701/\u5DDE").requiredOption("--city <city>", "\u57CE\u5E02").requiredOption("--address <address>", "\u8BE6\u7EC6\u5730\u5740").requiredOption("--promotion-link <url>", "\u63A8\u5E7F\u94FE\u63A5").requiredOption("--trade-id <name>", "\u884C\u4E1A\u540D\u79F0\uFF08\u6765\u81EA open-account bing-industries \u7684 name \u5B57\u6BB5\uFF09").requiredOption("--license-file <path>", "\u8425\u4E1A\u6267\u7167\u56FE\u7247\u672C\u5730\u8DEF\u5F84\uFF08JPG/PNG/PDF\uFF09").option("--postcode <code>", "\u90AE\u653F\u7F16\u7801").option("--advertiser-cid <cid>", "\u7ECF\u7406\u8D26\u6237 CID").option("--advertiser-name2 <name>", "\u7ECF\u7406\u8D26\u6237\u540D\u79F0").option("--account-count <n>", "\u5F00\u6237\u6570\u91CF\uFF081~6\uFF0C\u9ED8\u8BA4 1\uFF09", parseInt).option("--advertiser-id <magKey>", "\u53EF\u9009\uFF1A\u624B\u52A8\u6307\u5B9A\u5E7F\u544A\u4E3B\u7EC4 magKey\uFF08\u4E00\u822C\u65E0\u9700\u586B\u5199\uFF09").option("-t, --token <token>", "Auth Token").option("--verbose", "\u8BE6\u7EC6\u9519\u8BEF\u4FE1\u606F", false).action(async (opts) => {
8373
8490
  await runOpenAccountBing({
8374
8491
  token: opts.token,
8375
8492
  advertiserId: opts.advertiserId,
@@ -8411,7 +8528,7 @@ openAccountCmd.command("google-timezones").description("\u5217\u51FA Google \u5F
8411
8528
  openAccountCmd.command("google-wizard").description("\u4EA4\u4E92\u5F0F Google \u5F00\u6237\uFF08\u5BF9\u9F50\u7F51\u9875\u4E94\u6B65\u8BF4\u660E + \u4E24\u6B65\u8868\u5355\uFF1B\u9700\u7EC8\u7AEF TTY\uFF09").option("-t, --token <token>", "Auth Token").option("--verbose", "\u8BE6\u7EC6\u9519\u8BEF\u4FE1\u606F", false).action(async (opts) => {
8412
8529
  await runOpenAccountGoogleWizard({ token: opts.token, verbose: opts.verbose });
8413
8530
  });
8414
- openAccountCmd.command("google").description("\u63D0\u4EA4 Google \u5F00\u6237\u7533\u8BF7\uFF08\u4E0E\u7F51\u9875\u8868\u5355\u4E00\u81F4\uFF1A\u6309\u516C\u53F8\u540D\u79F0\u81EA\u52A8\u521B\u5EFA/\u5173\u8054\u5E7F\u544A\u4E3B\u7EC4\uFF0C\u65E0\u9700 magKey\uFF09").option("--advertiser-id <magKey>", "\u53EF\u9009\uFF1A\u624B\u52A8\u6307\u5B9A\u5E7F\u544A\u4E3B\u7EC4 ID\uFF08\u4E00\u822C\u4E0D\u7528\uFF1B\u8C03\u8BD5\u7528\u6216\u7279\u6B8A\u573A\u666F\uFF09").requiredOption("--account-name <name>", "\u5E7F\u544A\u8D26\u6237\u540D\u79F0\uFF08\u5EFA\u8BAE 22 \u4E2A\u5B57\u7B26\u4EE5\u5185\uFF09").requiredOption("--currency <code>", "\u8D27\u5E01\uFF1AUSD | CNY").requiredOption("--timezone <tz>", "\u65F6\u533A\uFF0C\u5982 Asia/Hong_Kong\uFF08\u53EF\u5148\u67E5 open-account google-timezones\uFF09").requiredOption("--invite-email <email>", "\u53D7\u9080\u7528\u6237\u90AE\u7BB1\uFF08\u8D26\u6237\u9080\u8BF7\u5C06\u53D1\u5230\u6B64\u90AE\u7BB1\uFF09").requiredOption("--company <name>", "\u516C\u53F8\u540D\u79F0\uFF08\u7528\u4E8E\u5339\u914D\u6216\u521B\u5EFA\u5E7F\u544A\u4E3B\u7EC4\uFF0C\u4E0E\u7F51\u9875\u7B2C\u4E00\u6B65\u4E00\u81F4\uFF09").option("--industry1 <level1>", "\u53EF\u9009\uFF1A\u884C\u4E1A\u4E00\u7EA7\uFF08\u7F51\u9875\u7AEF\u5DF2\u5F31\u5316\uFF0C\u591A\u6570\u573A\u666F\u53EF\u4E0D\u586B\uFF09").option("--industry2 <level2>", "\u53EF\u9009\uFF1A\u884C\u4E1A\u4E8C\u7EA7").requiredOption("--promotion-link <url>", "\u63A8\u5E7F\u94FE\u63A5\uFF08\u516C\u53F8\u5B98\u7F51\u6216\u4EA7\u54C1\u9875\uFF1B\u53EF\u5199\u57DF\u540D\uFF0C\u4F1A\u81EA\u52A8\u8865 https://\uFF09").requiredOption("--promotion-type <type>", "\u63A8\u5E7F\u7C7B\u578B\uFF1Ab2b | b2c | app").option("--invite-role <role>", "\u53D7\u9080\u7528\u6237\u89D2\u8272\uFF1AStandard | Admin\uFF08\u9ED8\u8BA4 Standard\uFF09", "Standard").option("--auto-mailbox", "\u81EA\u52A8\u5206\u914D\u90AE\u7BB1", false).option("--counts <n>", "\u672C\u6B21\u5F00\u6237\u6570\u91CF\uFF081-3\uFF0C\u9ED8\u8BA4 1\uFF09", parseInt).option("--manager-customer-id <id>", "MCC \u7ECF\u7406\u8D26\u6237 ID\uFF08\u53EF\u9009\uFF09").option("-t, --token <token>", "Auth Token").option("--verbose", "\u8BE6\u7EC6\u9519\u8BEF\u4FE1\u606F", false).action(async (opts) => {
8531
+ openAccountCmd.command("google").description("\u63D0\u4EA4 Google \u5F00\u6237\u7533\u8BF7\uFF08\u4E0E\u7F51\u9875\u8868\u5355\u4E00\u81F4\uFF1A\u6309\u516C\u53F8\u540D\u79F0\u81EA\u52A8\u521B\u5EFA/\u5173\u8054\u5E7F\u544A\u4E3B\u7EC4\uFF0C\u65E0\u9700 magKey\uFF09").option("--advertiser-id <magKey>", "\u53EF\u9009\uFF1A\u624B\u52A8\u6307\u5B9A\u5E7F\u544A\u4E3B\u7EC4 ID\uFF08\u4E00\u822C\u4E0D\u7528\uFF1B\u8C03\u8BD5\u7528\u6216\u7279\u6B8A\u573A\u666F\uFF09").requiredOption("--account-name <name>", "\u5E7F\u544A\u8D26\u6237\u540D\u79F0\uFF08\u5EFA\u8BAE 22 \u4E2A\u5B57\u7B26\u4EE5\u5185\uFF09").requiredOption("--currency <code>", "\u8D27\u5E01\uFF1AUSD | CNY").requiredOption("--timezone <tz>", "\u65F6\u533A\uFF0C\u5982 Asia/Hong_Kong\uFF08\u53EF\u5148\u67E5 open-account google-timezones\uFF09").requiredOption("--invite-email <email>", "\u53D7\u9080\u7528\u6237\u90AE\u7BB1\uFF08\u8D26\u6237\u9080\u8BF7\u5C06\u53D1\u5230\u6B64\u90AE\u7BB1\uFF09").requiredOption("--company <name>", "\u516C\u53F8\u540D\u79F0\uFF08\u7528\u4E8E\u5339\u914D\u6216\u521B\u5EFA\u5E7F\u544A\u4E3B\u7EC4\uFF0C\u4E0E\u7F51\u9875\u7B2C\u4E00\u6B65\u4E00\u81F4\uFF09").requiredOption("--promotion-link <url>", "\u63A8\u5E7F\u94FE\u63A5\uFF08\u516C\u53F8\u5B98\u7F51\u6216\u4EA7\u54C1\u9875\uFF1B\u53EF\u5199\u57DF\u540D\uFF0C\u4F1A\u81EA\u52A8\u8865 https://\uFF09").requiredOption("--promotion-type <type>", "\u63A8\u5E7F\u7C7B\u578B\uFF1Ab2b | b2c | app").option("--invite-role <role>", "\u53D7\u9080\u7528\u6237\u89D2\u8272\uFF1AStandard | Admin\uFF08\u9ED8\u8BA4 Standard\uFF09", "Standard").option("--auto-mailbox", "\u81EA\u52A8\u5206\u914D\u90AE\u7BB1", false).option("--counts <n>", "\u672C\u6B21\u5F00\u6237\u6570\u91CF\uFF081-3\uFF0C\u9ED8\u8BA4 1\uFF09", parseInt).option("--manager-customer-id <id>", "MCC \u7ECF\u7406\u8D26\u6237 ID\uFF08\u53EF\u9009\uFF09").option("-t, --token <token>", "Auth Token").option("--verbose", "\u8BE6\u7EC6\u9519\u8BEF\u4FE1\u606F", false).action(async (opts) => {
8415
8532
  await runOpenAccountGoogle({
8416
8533
  token: opts.token,
8417
8534
  advertiserId: opts.advertiserId,
@@ -8420,8 +8537,6 @@ openAccountCmd.command("google").description("\u63D0\u4EA4 Google \u5F00\u6237\u
8420
8537
  timezone: opts.timezone,
8421
8538
  inviteEmail: opts.inviteEmail,
8422
8539
  company: opts.company,
8423
- industryLevel1: opts.industry1,
8424
- industryLevel2: opts.industry2,
8425
8540
  promotionLink: opts.promotionLink,
8426
8541
  promotionType: opts.promotionType,
8427
8542
  inviteRole: opts.inviteRole,
@@ -8431,12 +8546,11 @@ openAccountCmd.command("google").description("\u63D0\u4EA4 Google \u5F00\u6237\u
8431
8546
  verbose: opts.verbose
8432
8547
  });
8433
8548
  });
8434
- openAccountCmd.command("tiktok").description("\u63D0\u4EA4 TikTok \u5F00\u6237\u7533\u8BF7\uFF08\u9700\u4E0A\u4F20\u8425\u4E1A\u6267\u7167\u56FE\u7247\uFF0C\u6309\u516C\u53F8\u540D\u81EA\u52A8\u521B\u5EFA/\u5173\u8054\u5E7F\u544A\u4E3B\u7EC4\uFF09").requiredOption("--account-name <name>", "\u5E7F\u544A\u8D26\u6237\u540D\u79F0").requiredOption("--currency <code>", "\u8D27\u5E01\uFF1AUSD | CNY").requiredOption("--timezone <tz>", "\u65F6\u533A\uFF0C\u5982 UTC+8\uFF08\u53EF\u5148\u67E5 open-account tiktok-timezones\uFF09").requiredOption("--company <name>", "\u516C\u53F8\u540D\u79F0").requiredOption("--industry-id <id>", "\u884C\u4E1A ID\uFF08\u6570\u5B57\uFF0CTikTok \u884C\u4E1A\u53F6\u5B50\u8282\u70B9 ID\uFF09", parseInt).requiredOption("--registered-area <code>", "\u6CE8\u518C\u5730\u56FD\u5BB6\u4EE3\u7801\uFF0C\u5982 CN").requiredOption("--promotion-link <url>", "\u63A8\u5E7F\u94FE\u63A5").requiredOption("--license-no <no>", "\u8425\u4E1A\u6267\u7167\u7F16\u7801\uFF08\u793E\u4F1A\u7EDF\u4E00\u4FE1\u7528\u4EE3\u7801\uFF09").requiredOption("--license-file <path>", "\u8425\u4E1A\u6267\u7167\u56FE\u7247\u672C\u5730\u8DEF\u5F84\uFF08JPG/PNG\uFF09").option("--bc-type <type>", "\u4E1A\u52A1\u4E2D\u5FC3\u7C7B\u578B\uFF1AShop | Store | App | B2B | Other\uFF08\u9ED8\u8BA4 Shop\uFF09", "Shop").option("--partner-id <id>", "BC \u5408\u4F5C\u4F19\u4F34 ID\uFF08bc_id\uFF0C\u53EF\u9009\uFF09").option("--counts <n>", "\u672C\u6B21\u5F00\u6237\u6570\u91CF\uFF081-10\uFF0C\u9ED8\u8BA4 1\uFF09", parseInt).option("--representative-name <name>", "\u6CD5\u4EBA\u59D3\u540D\uFF08\u82E5\u9700\u8981\u94F6\u8054\u9A8C\u8BC1\u65F6\u586B\u5199\uFF09").option("--representative-id <id>", "\u6CD5\u4EBA\u8EAB\u4EFD\u8BC1\u53F7").option("--unionpay-account <no>", "\u6CD5\u4EBA\u94F6\u8054\u8D26\u53F7").option("--representative-phone <phone>", "\u6CD5\u4EBA\u624B\u673A\u53F7").option("--advertiser-id <magKey>", "\u53EF\u9009\uFF1A\u624B\u52A8\u6307\u5B9A\u5E7F\u544A\u4E3B\u7EC4 magKey\uFF08\u4E00\u822C\u65E0\u9700\u586B\u5199\uFF09").option("-t, --token <token>", "Auth Token").option("--verbose", "\u8BE6\u7EC6\u9519\u8BEF\u4FE1\u606F", false).action(async (opts) => {
8549
+ openAccountCmd.command("tiktok").description("\u63D0\u4EA4 TikTok \u5F00\u6237\u7533\u8BF7\uFF08\u9700\u4E0A\u4F20\u8425\u4E1A\u6267\u7167\u56FE\u7247\uFF0C\u6309\u516C\u53F8\u540D\u81EA\u52A8\u521B\u5EFA/\u5173\u8054\u5E7F\u544A\u4E3B\u7EC4\uFF09").requiredOption("--account-name <name>", "\u5E7F\u544A\u8D26\u6237\u540D\u79F0").requiredOption("--timezone <tz>", "\u65F6\u533A\uFF0C\u5982 UTC+8\uFF08\u53EF\u5148\u67E5 open-account tiktok-timezones\uFF09").requiredOption("--company <name>", "\u516C\u53F8\u540D\u79F0").requiredOption("--industry-id <id>", "\u884C\u4E1A ID\uFF08\u6570\u5B57\uFF0CTikTok \u884C\u4E1A\u53F6\u5B50\u8282\u70B9 ID\uFF09", parseInt).requiredOption("--registered-area <code>", "\u6CE8\u518C\u5730\u56FD\u5BB6\u4EE3\u7801\uFF0C\u5982 CN").requiredOption("--promotion-link <url>", "\u63A8\u5E7F\u94FE\u63A5").requiredOption("--license-no <no>", "\u8425\u4E1A\u6267\u7167\u7F16\u7801\uFF08\u793E\u4F1A\u7EDF\u4E00\u4FE1\u7528\u4EE3\u7801\uFF09").requiredOption("--license-file <path>", "\u8425\u4E1A\u6267\u7167\u56FE\u7247\u672C\u5730\u8DEF\u5F84\uFF08JPG/PNG\uFF09").option("--bc-type <type>", "\u4E1A\u52A1\u4E2D\u5FC3\u7C7B\u578B\uFF1AShop | Store | App | B2B | Other\uFF08\u9ED8\u8BA4 Shop\uFF09", "Shop").option("--partner-id <id>", "BC \u5408\u4F5C\u4F19\u4F34 ID\uFF08bc_id\uFF0C\u53EF\u9009\uFF09").option("--counts <n>", "\u672C\u6B21\u5F00\u6237\u6570\u91CF\uFF081-10\uFF0C\u9ED8\u8BA4 1\uFF09", parseInt).requiredOption("--representative-name <name>", "\u6CD5\u4EBA\u59D3\u540D\uFF08\u94F6\u8054\u9A8C\u8BC1\u5FC5\u586B\uFF09").requiredOption("--representative-id <id>", "\u6CD5\u4EBA\u8EAB\u4EFD\u8BC1\u53F7\uFF08\u94F6\u8054\u9A8C\u8BC1\u5FC5\u586B\uFF09").requiredOption("--unionpay-account <no>", "\u6CD5\u4EBA\u94F6\u8054\u8D26\u53F7\uFF08\u94F6\u8054\u9A8C\u8BC1\u5FC5\u586B\uFF09").requiredOption("--representative-phone <phone>", "\u6CD5\u4EBA\u624B\u673A\u53F7\uFF08\u94F6\u8054\u9A8C\u8BC1\u5FC5\u586B\uFF09").option("--advertiser-id <magKey>", "\u53EF\u9009\uFF1A\u624B\u52A8\u6307\u5B9A\u5E7F\u544A\u4E3B\u7EC4 magKey\uFF08\u4E00\u822C\u65E0\u9700\u586B\u5199\uFF09").option("-t, --token <token>", "Auth Token").option("--verbose", "\u8BE6\u7EC6\u9519\u8BEF\u4FE1\u606F", false).action(async (opts) => {
8435
8550
  await runOpenAccountTikTok({
8436
8551
  token: opts.token,
8437
8552
  advertiserId: opts.advertiserId,
8438
8553
  accountName: opts.accountName,
8439
- currency: opts.currency,
8440
8554
  timezone: opts.timezone,
8441
8555
  company: opts.company,
8442
8556
  industryId: opts.industryId,
@@ -8484,6 +8598,10 @@ program.command("diagnostic").description("Google \u5E7F\u544A\u8BCA\u65AD\uFF1A
8484
8598
  console.log(" \u8BCA\u65AD\u5165\u53E3\uFF08\u6D4B\u8BD5\uFF09\uFF1Ahttps://www-ci.siluzan.com/diagnostics/\n");
8485
8599
  console.log(" \u8BF7\u5728\u6D4F\u89C8\u5668\u4E2D\u6253\u5F00\u4E0A\u8FF0\u5730\u5740\u5B8C\u6210\u8BCA\u65AD\u64CD\u4F5C\u3002\n");
8486
8600
  });
8601
+ var activeCmd = process.argv[2];
8487
8602
  program.parseAsync().then(() => {
8488
- notifyIfOutdated();
8603
+ if (activeCmd !== "update" && activeCmd !== "login") {
8604
+ notifyIfOutdated().catch(() => {
8605
+ });
8606
+ }
8489
8607
  });
@@ -115,6 +115,29 @@ description: >-
115
115
 
116
116
  ---
117
117
 
118
+ ## 自动更新规则(必须遵守)
119
+
120
+ CLI 命令执行后可能在输出末尾出现以下两种标记,Agent 必须按对应规则处理:
121
+
122
+ ### `[SILUZAN_FORCE_UPDATE]` — 强制更新(必须,不可跳过)
123
+
124
+ 当前版本已低于最低要求版本,**必须先升级再继续**:
125
+
126
+ 1. 立即执行 `siluzan-tso update`
127
+ 2. 告知用户:当前版本过旧,已自动升级,请重新执行刚才的操作
128
+ 3. 不得在升级前继续执行任何业务命令
129
+
130
+ ### `[SILUZAN_UPDATE_AVAILABLE]` — 可选更新
131
+
132
+ 有新版本可用,但当前版本仍可正常使用:
133
+
134
+ 1. 执行 `siluzan-tso update`
135
+ 2. 更新完成后继续执行原本的用户任务
136
+
137
+ > 两种情况下,`siluzan-tso update` 都会同步刷新 Skill 文件,新版本可能包含参数或流程变更。
138
+
139
+ ---
140
+
118
141
  ## AI 行为规范
119
142
 
120
143
  - **先查账户再操作**:对具体账户做操作前,先通过 `list-accounts` 确认 ID。
@@ -1,5 +1,5 @@
1
1
  {
2
2
  "slug": "siluzan-tso",
3
- "version": "1.0.0-beta.20",
4
- "publishedAt": 1774406904337
3
+ "version": "1.0.0-beta.22",
4
+ "publishedAt": 1774420324671
5
5
  }
@@ -73,20 +73,21 @@ siluzan-tso config show
73
73
 
74
74
  ## 更新 CLI 与 Skill 文件
75
75
 
76
- CLI 在每次命令执行后会静默检查新版本,有更新时提示:
76
+ CLI 在每次命令执行后会静默检查新版本,有更新时提示;**推荐一键升级并刷新 Skill**:
77
77
 
78
- ```
79
- 💡 发现新版本 x.x.x,运行以下命令升级:
80
- npm install -g siluzan-tso-cli@beta
78
+ ```bash
79
+ siluzan-tso update
81
80
  ```
82
81
 
83
- 发现提示后主动执行:
82
+ 等价于:拉取 npm 最新版(全局安装时)+ 按 `tsoInstalledTargets` 强制重写各平台 Skill。也可手动:
84
83
 
85
84
  ```bash
86
- npm install -g siluzan-tso-cli@beta # 升级 CLI
87
- siluzan-tso init --force # 重新写入 Skill 文件(--ai 与初始安装时一致)
85
+ npm install -g siluzan-tso-cli@beta # 仅升级 CLI
86
+ siluzan-tso init --force # 再按上次记录刷新(--ai 与初始安装时一致)
88
87
  ```
89
88
 
89
+ `update` 也支持 `--force`(不比较版本,强制重装并刷新)、`--skip-init`(只跑 npm 不刷新 Skill)。
90
+
90
91
  已安装过的目标平台记录在 `~/.siluzan/config.json` 的 `tsoInstalledTargets` 字段,可用于确认需要更新哪些平台。
91
92
 
92
93
  ---
@@ -100,20 +101,3 @@ siluzan-tso config clear # 清空所有凭据
100
101
  ```
101
102
 
102
103
  ---
103
-
104
- ## 环境切换
105
-
106
- | 配置项 | 生产环境(默认) | 测试环境 |
107
- |--------|----------------|---------|
108
- | `apiBaseUrl` | `https://tso-api.siluzan.com` | `https://tso-api-ci.siluzan.com` |
109
- | `googleApiUrl` | `https://googleapi.mysiluzan.com` | `https://googleapi-ci.mysiluzan.com` |
110
-
111
- ```bash
112
- # 切换到测试环境
113
- siluzan-tso config set --api-base https://tso-api-ci.siluzan.com
114
- siluzan-tso config set --google-api https://googleapi-ci.mysiluzan.com
115
-
116
- # 切回生产环境
117
- siluzan-tso config set --api-base https://tso-api.siluzan.com
118
- siluzan-tso config set --google-api https://googleapi.mysiluzan.com
119
- ```
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "siluzan-tso-cli",
3
- "version": "1.0.0-beta.20",
3
+ "version": "1.0.0-beta.22",
4
4
  "description": "Siluzan 广告账户管理 CLI — 查询账户、余额、消耗数据,管理绑定关系与充值。",
5
5
  "type": "module",
6
6
  "bin": {
@@ -8,7 +8,8 @@
8
8
  },
9
9
  "files": [
10
10
  "dist",
11
- "README.md"
11
+ "README.md",
12
+ "scripts/postinstall.mjs"
12
13
  ],
13
14
  "keywords": [
14
15
  "siluzan",
@@ -40,6 +41,7 @@
40
41
  "node": ">=18"
41
42
  },
42
43
  "scripts": {
44
+ "postinstall": "node scripts/postinstall.mjs",
43
45
  "build": "node scripts/write-defaults.mjs --env production && tsup && node scripts/copy-skill-assets.mjs --env production",
44
46
  "build:prod": "node scripts/write-defaults.mjs --env production && tsup && node scripts/copy-skill-assets.mjs --env production",
45
47
  "build:test": "node scripts/write-defaults.mjs --env test && tsup && node scripts/copy-skill-assets.mjs --env test",
@@ -0,0 +1,5 @@
1
+ /**
2
+ * 安装本包后提示:升级 CLI 后请同步 Skill(见 siluzan-tso update)。
3
+ * monorepo / 本地 link 时每次 install 也会执行,属预期。
4
+ */
5
+ console.log("siluzan-tso-cli: 升级后请将项目中的Skill文件复制到对应平台的Skill目录下,或者执行 siluzan-tso update 命令。");