siluzan-tso-cli 1.1.18-beta.7 → 1.1.18-beta.8

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.18-beta.7),供内部测试使用。正式发布后安装命令将改为 `npm install -g siluzan-tso-cli`。
54
+ > **注意**:当前为测试版(1.1.18-beta.8),供内部测试使用。正式发布后安装命令将改为 `npm install -g siluzan-tso-cli`。
55
55
 
56
56
  | 助手 | 建议 `--ai` |
57
57
  | ----------------------- | ------------------------------------ |
package/dist/index.js CHANGED
@@ -2559,16 +2559,11 @@ function printAuthMissingHelp(binName) {
2559
2559
  `
2560
2560
  \u274C \u672A\u627E\u5230\u8BA4\u8BC1\u51ED\u636E\u3002\u8BF7\u9009\u62E9\u4EE5\u4E0B\u4EFB\u610F\u4E00\u79CD\u65B9\u5F0F\uFF1A
2561
2561
 
2562
- \u65B9\u5F0F\u4E00\uFF08\u63A8\u8350\uFF09\uFF1AAPI Key
2563
- \u5728\u4E1D\u8DEF\u8D5E\u300C\u8BBE\u7F6E \u2192 API Key \u7BA1\u7406\u300D\u521B\u5EFA API Key\uFF0C\u7136\u540E\u8FD0\u884C\uFF1A
2564
- ${binName} login --api-key <YOUR_API_KEY>
2565
-
2566
- \u65B9\u5F0F\u4E8C\uFF1A\u73AF\u5883\u53D8\u91CF\uFF08CI/CD \u63A8\u8350\uFF09
2567
- export SILUZAN_API_KEY=<YOUR_API_KEY>
2568
- # \u6216 export SILUZAN_AUTH_TOKEN=<YOUR_TOKEN>
2569
-
2570
- \u65B9\u5F0F\u4E09\uFF1AJWT Token
2571
- ${binName} login
2562
+ \u8BF7\u4F7F\u7528\u624B\u673A\u53F7\u91CD\u65B0\u767B\u5F55
2563
+ ${binName} send-login-code --phone <YOUR_PHONE>
2564
+ `,
2565
+ `\u7136\u540E\u4F7F\u7528\u6536\u5230\u7684\u9A8C\u8BC1\u7801\u5B8C\u6210\u767B\u5F55
2566
+ ${binName} login --phone <YOUR_PHONE> --code <YOUR_CODE>
2572
2567
  `
2573
2568
  );
2574
2569
  process.exit(1);
@@ -3060,6 +3055,35 @@ function normalizeChinaPhone(input) {
3060
3055
  function isValidChinaPhone(input) {
3061
3056
  return /^\+861\d{10}$/.test(normalizeChinaPhone(input));
3062
3057
  }
3058
+ async function sendPhoneLoginCode(opts) {
3059
+ const phone = normalizeChinaPhone(opts.phone);
3060
+ const url = `${opts.ssoBaseUrl}/Account/SendVaildCode?Phone=${encodeURIComponent(
3061
+ phone
3062
+ )}&RandStr=&Iicket=`;
3063
+ if (opts.verbose) {
3064
+ process.stderr.write(`[phone-login] GET ${url}
3065
+ `);
3066
+ }
3067
+ const res = await rawRequest(url, {
3068
+ method: "GET",
3069
+ headers: {
3070
+ Accept: "application/json",
3071
+ "Accept-Language": "zh-CN"
3072
+ }
3073
+ });
3074
+ if (res.status < 200 || res.status >= 300) {
3075
+ return { ok: false, message: `HTTP ${res.status}` };
3076
+ }
3077
+ let body;
3078
+ try {
3079
+ body = JSON.parse(res.text);
3080
+ } catch {
3081
+ return { ok: false, message: `\u54CD\u5E94\u975E JSON\uFF1A${res.text.slice(0, 120)}` };
3082
+ }
3083
+ const state = (body.State ?? body.state ?? "").toLowerCase();
3084
+ const message = body.Message ?? body.message ?? "";
3085
+ return { ok: state === "ok", message };
3086
+ }
3063
3087
  async function loginByPhoneCode(opts) {
3064
3088
  const phone = normalizeChinaPhone(opts.phone);
3065
3089
  const url = `${opts.ssoBaseUrl}/Account/LoginByMiniCode?phone=${encodeURIComponent(
@@ -6620,17 +6644,38 @@ function validateAndNormalizePhone(rawInput) {
6620
6644
  }
6621
6645
  return normalizeChinaPhone(rawPhone);
6622
6646
  }
6623
- async function runPhoneLogin(opts) {
6647
+ async function runSendLoginCode(opts) {
6624
6648
  const phone = validateAndNormalizePhone(opts.phone);
6625
- const code = opts.code?.trim() ?? "";
6626
- if (!code) {
6627
- console.error("\n\u274C \u7F3A\u5C11 --code \u53C2\u6570\u3002\u624B\u673A\u53F7\u767B\u5F55\u662F\u4E24\u6BB5\u5F0F\u8C03\u7528\uFF0C\u8BF7\u6309\u987A\u5E8F\u6267\u884C\uFF1A\n");
6628
- console.error(` 1) siluzan-tso send-login-code --phone ${phone}`);
6629
- console.error(` 2) siluzan-tso login --phone ${phone} --code <\u6536\u5230\u76846\u4F4D\u9A8C\u8BC1\u7801>
6649
+ const tsoApiBase = process.env.SILUZAN_TSO_API_BASE ?? DEFAULT_API_BASE;
6650
+ const ssoBaseUrl = deriveSsoBaseUrl(tsoApiBase);
6651
+ console.log("\n\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550");
6652
+ console.log(" Siluzan \u53D1\u9001\u767B\u5F55\u77ED\u4FE1\u9A8C\u8BC1\u7801");
6653
+ console.log("\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\n");
6654
+ console.log(` \u624B\u673A\u53F7 : ${phone}`);
6655
+ console.log("\u2192 \u6B63\u5728\u5411\u624B\u673A\u53D1\u9001\u9A8C\u8BC1\u7801...");
6656
+ const r = await sendPhoneLoginCode({ ssoBaseUrl, phone, verbose: opts.verbose });
6657
+ if (!r.ok) {
6658
+ console.error(`
6659
+ \u274C \u77ED\u4FE1\u9A8C\u8BC1\u7801\u53D1\u9001\u5931\u8D25\uFF1A${r.message || "(\u540E\u7AEF\u672A\u8FD4\u56DE\u539F\u56E0)"}
6630
6660
  `);
6631
- console.error("\uFF08\u62C6\u6210\u4E24\u6B65\u662F\u4E3A\u4E86\u907F\u514D AI Agent \u5361\u5728\u4EA4\u4E92\u8F93\u5165\u65F6\u53CD\u590D\u91CD\u8BD5\u5BFC\u81F4\u77ED\u4FE1\u8F70\u70B8\uFF09\n");
6632
6661
  process.exit(1);
6633
6662
  }
6663
+ console.log(`\u2713 \u9A8C\u8BC1\u7801\u5DF2\u53D1\u9001\uFF0810 \u5206\u949F\u5185\u6709\u6548\uFF09\u3002
6664
+ `);
6665
+ console.log("\u4E0B\u4E00\u6B65\uFF1A\u62FF\u5230 6 \u4F4D\u9A8C\u8BC1\u7801\u540E\uFF0C\u8FD0\u884C\u4E0B\u9762\u7684\u547D\u4EE4\u5B8C\u6210\u767B\u5F55\uFF1A\n");
6666
+ console.log(` siluzan-tso login --phone ${phone} --code <6\u4F4D\u9A8C\u8BC1\u7801>
6667
+ `);
6668
+ console.log("\u53EF\u9009\u53C2\u6570\uFF1A--name / --valid-days / --expires-at / --services");
6669
+ console.log("\uFF08\u9ED8\u8BA4\u521B\u5EFA 90 \u5929\u6709\u6548\u3001\u52FE\u9009 TSO + CUT \u670D\u52A1\u7684 API Key\uFF09\n");
6670
+ }
6671
+ function normalizeBearerTokenInput(raw) {
6672
+ const t = raw.trim();
6673
+ if (/^bearer\s+/i.test(t)) {
6674
+ return t.replace(/^bearer\s+/i, "").trim();
6675
+ }
6676
+ return t;
6677
+ }
6678
+ async function executePhoneLoginWithVerifiedCode(phone, code, opts) {
6634
6679
  let allowedServices;
6635
6680
  try {
6636
6681
  allowedServices = parseAllowedServices(opts.services);
@@ -6702,9 +6747,10 @@ async function runPhoneLogin(opts) {
6702
6747
  `
6703
6748
  \u8BE5\u624B\u673A\u53F7\u5C1A\u672A\u6CE8\u518C\u4E1D\u8DEF\u8D5E\u8D26\u53F7\u3002\u8BF7\u5148\u5728\u7F51\u9875\u7AEF\u6CE8\u518C\uFF1A
6704
6749
  ${WEB_BASE_URL}
6705
- \u6CE8\u518C\u6210\u529F\u540E\u518D\u56DE\u5230 CLI \u91CD\u8BD5\u4E24\u6BB5\u5F0F\u767B\u5F55\uFF1A
6750
+ \u6CE8\u518C\u6210\u529F\u540E\u518D\u56DE\u5230 CLI \u91CD\u8BD5\uFF1A
6706
6751
  siluzan-tso send-login-code --phone ${phone}
6707
6752
  siluzan-tso login --phone ${phone} --code <6\u4F4D\u9A8C\u8BC1\u7801>
6753
+ \u6216\u5728\u7EC8\u7AEF\u6267\u884C\u65E0\u53C2 siluzan-tso login \u540E\u9009\u62E9\u300C\u624B\u673A\u53F7\u767B\u5F55\u300D\u3002
6708
6754
  `
6709
6755
  );
6710
6756
  } else if (/验证码错误|验证码/.test(msg)) {
@@ -6718,34 +6764,72 @@ async function runPhoneLogin(opts) {
6718
6764
  process.exit(1);
6719
6765
  }
6720
6766
  }
6721
- async function runLogin(opts = {}) {
6722
- if (opts.apiKey !== void 0) {
6723
- const key = opts.apiKey.trim();
6724
- if (!key) {
6725
- console.error("\n\u274C API Key \u4E0D\u80FD\u4E3A\u7A7A\u3002\n");
6767
+ async function runPhoneLogin(opts) {
6768
+ const phone = validateAndNormalizePhone(opts.phone);
6769
+ const code = opts.code?.trim() ?? "";
6770
+ if (!code) {
6771
+ console.error("\n\u274C \u7F3A\u5C11 --code \u53C2\u6570\u3002\u624B\u673A\u53F7\u767B\u5F55\u662F\u4E24\u6BB5\u5F0F\u8C03\u7528\uFF0C\u8BF7\u6309\u987A\u5E8F\u6267\u884C\uFF1A\n");
6772
+ console.error(` 1) siluzan-tso send-login-code --phone ${phone}`);
6773
+ console.error(` 2) siluzan-tso login --phone ${phone} --code <\u6536\u5230\u76846\u4F4D\u9A8C\u8BC1\u7801>
6774
+ `);
6775
+ console.error("\uFF08\u62C6\u6210\u4E24\u6B65\u662F\u4E3A\u4E86\u907F\u514D AI Agent \u5361\u5728\u4EA4\u4E92\u8F93\u5165\u65F6\u53CD\u590D\u91CD\u8BD5\u5BFC\u81F4\u77ED\u4FE1\u8F70\u70B8\uFF09\n");
6776
+ console.error("\u6216\u5728\u7EC8\u7AEF\u6267\u884C\uFF1Asiluzan-tso login\uFF08\u65E0\u53C2\u6570\uFF09\uFF0C\u9009\u62E9\u300C\u624B\u673A\u53F7 + \u77ED\u4FE1\u9A8C\u8BC1\u7801\u300D\u7531\u672C CLI \u53D1\u7801\u3002\n");
6777
+ process.exit(1);
6778
+ }
6779
+ await executePhoneLoginWithVerifiedCode(phone, code, opts);
6780
+ }
6781
+ async function runInteractiveJwtLogin() {
6782
+ const shared = readSharedConfig();
6783
+ const rl = readline.createInterface({ input: process.stdin, output: process.stdout });
6784
+ const prompt = (q) => new Promise((res) => rl.question(q, (a) => res(a.trim())));
6785
+ try {
6786
+ if (shared.apiKey) {
6787
+ console.log(`
6788
+ \u5DF2\u4FDD\u5B58 API Key\uFF08${maskSecret(shared.apiKey)}\uFF09\u3002\u6539\u7528 JWT \u5C06\u6E05\u9664 API Key \u5E76\u4F18\u5148\u4F7F\u7528 Bearer Token\u3002`);
6789
+ const ans = await prompt("\u662F\u5426\u7EE7\u7EED\uFF1F(y/N) ");
6790
+ if (ans.toLowerCase() !== "y") {
6791
+ console.log("\n\u5DF2\u53D6\u6D88\u3002\n");
6792
+ return;
6793
+ }
6794
+ }
6795
+ console.log("\n\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550");
6796
+ console.log(" Siluzan \u767B\u5F55\uFF08JWT Token\uFF09");
6797
+ console.log("\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550");
6798
+ console.log("\n\u8BF7\u7C98\u8D34 access token\uFF08\u53EF\u5E26\u6216\u4E0D\u5E26 `Bearer ` \u524D\u7F00\uFF09\uFF1A\n");
6799
+ let token = "";
6800
+ for (let i = 0; i < 3; i++) {
6801
+ const input = await prompt("Token\uFF1A");
6802
+ const normalized = normalizeBearerTokenInput(input);
6803
+ if (normalized) {
6804
+ token = normalized;
6805
+ break;
6806
+ }
6807
+ console.log("\u274C Token \u4E0D\u80FD\u4E3A\u7A7A\uFF0C\u8BF7\u91CD\u8BD5");
6808
+ }
6809
+ if (!token) {
6810
+ console.error("\n\u274C \u591A\u6B21\u8F93\u5165\u65E0\u6548\u3002\n");
6726
6811
  process.exit(1);
6727
6812
  }
6728
- writeSharedConfig({ apiKey: key });
6813
+ writeSharedConfig({ authToken: token, apiKey: "" });
6729
6814
  console.log(`
6730
- \u2705 API Key \u5DF2\u4FDD\u5B58\uFF08${maskSecret(key)}\uFF09`);
6815
+ \u2705 JWT \u5DF2\u4FDD\u5B58\uFF08${maskSecret(token)}\uFF09`);
6731
6816
  console.log(` \u914D\u7F6E\u6587\u4EF6\uFF1A${CONFIG_FILE}`);
6732
6817
  printPostLoginReminderBanner();
6733
6818
  console.log("\u73B0\u5728\u53EF\u4EE5\u8FD0\u884C siluzan-tso -h \u547D\u4EE4\u67E5\u770B\u5E2E\u52A9\u4E86\n");
6734
- return;
6735
- }
6736
- if (opts.phone !== void 0) {
6737
- await runPhoneLogin(opts);
6738
- return;
6819
+ } finally {
6820
+ rl.close();
6739
6821
  }
6822
+ }
6823
+ async function runInteractiveApiKeyLogin() {
6740
6824
  const shared = readSharedConfig();
6741
6825
  const currentKey = shared.apiKey ?? "";
6742
6826
  if (currentKey) {
6743
6827
  console.log(`
6744
6828
  \u5DF2\u68C0\u6D4B\u5230\u5DF2\u4FDD\u5B58\u7684 API Key\uFF08${maskSecret(currentKey)}\uFF09\u3002`);
6745
- const rl2 = readline.createInterface({ input: process.stdin, output: process.stdout });
6829
+ const rl0 = readline.createInterface({ input: process.stdin, output: process.stdout });
6746
6830
  const answer = await new Promise(
6747
- (res) => rl2.question("\u662F\u5426\u8986\u76D6\uFF1F(y/N) ", (a) => {
6748
- rl2.close();
6831
+ (res) => rl0.question("\u662F\u5426\u8986\u76D6\uFF1F(y/N) ", (a) => {
6832
+ rl0.close();
6749
6833
  res(a.trim());
6750
6834
  })
6751
6835
  );
@@ -6756,46 +6840,164 @@ async function runLogin(opts = {}) {
6756
6840
  }
6757
6841
  const rl = readline.createInterface({ input: process.stdin, output: process.stdout });
6758
6842
  const prompt = (q) => new Promise((res) => rl.question(q, (a) => res(a.trim())));
6759
- console.log("\n\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550");
6760
- console.log(" Siluzan \u767B\u5F55\uFF08API Key\uFF09");
6761
- console.log("\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550");
6762
- console.log("\n\u8BF7\u6309\u4EE5\u4E0B\u6B65\u9AA4\u83B7\u53D6 API Key\uFF1A");
6763
- console.log("\n 1. \u5728\u6D4F\u89C8\u5668\u4E2D\u6253\u5F00\u4EE5\u4E0B\u5730\u5740\uFF08\u9700\u5DF2\u767B\u5F55\u4E1D\u8DEF\u8D5E\u8D26\u53F7\uFF09\uFF1A");
6764
- console.log(`
6843
+ try {
6844
+ console.log("\n\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550");
6845
+ console.log(" Siluzan \u767B\u5F55\uFF08API Key\uFF09");
6846
+ console.log("\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550");
6847
+ console.log("\n\u8BF7\u6309\u4EE5\u4E0B\u6B65\u9AA4\u83B7\u53D6 API Key\uFF1A");
6848
+ console.log("\n 1. \u5728\u6D4F\u89C8\u5668\u4E2D\u6253\u5F00\u4EE5\u4E0B\u5730\u5740\uFF08\u9700\u5DF2\u767B\u5F55\u4E1D\u8DEF\u8D5E\u8D26\u53F7\uFF09\uFF1A");
6849
+ console.log(`
6765
6850
  ${API_KEY_MANAGEMENT_URL}
6766
6851
  `);
6767
- console.log(
6768
- " 2. \u70B9\u51FB\u300C\u521B\u5EFA API Key\u300D\u6309\u94AE\u751F\u6210\u4E00\u4E2A\u65B0\u7684 Key, \u52FE\u9009TSO (\u5E7F\u544A\u6295\u653E\u670D\u52A1)\u4E0ESUCAI (\u7D20\u6750\u4E2D\u5FC3\u670D\u52A1)"
6769
- );
6770
- console.log(" 3. \u590D\u5236\u751F\u6210\u7684 API Key");
6771
- console.log(" 4. \u7C98\u8D34\u5230\u4E0B\u65B9\u5E76\u6309\u56DE\u8F66\n");
6772
- let apiKey = "";
6773
- for (let i = 0; i < 3; i++) {
6774
- const input = await prompt("\u7C98\u8D34 API Key\uFF1A");
6775
- if (input) {
6776
- apiKey = input;
6852
+ console.log(
6853
+ " 2. \u70B9\u51FB\u300C\u521B\u5EFA API Key\u300D\u6309\u94AE\u751F\u6210\u4E00\u4E2A\u65B0\u7684 Key, \u52FE\u9009TSO (\u5E7F\u544A\u6295\u653E\u670D\u52A1)\u4E0ESUCAI (\u7D20\u6750\u4E2D\u5FC3\u670D\u52A1)"
6854
+ );
6855
+ console.log(" 3. \u590D\u5236\u751F\u6210\u7684 API Key");
6856
+ console.log(" 4. \u7C98\u8D34\u5230\u4E0B\u65B9\u5E76\u6309\u56DE\u8F66\n");
6857
+ let apiKey = "";
6858
+ for (let i = 0; i < 3; i++) {
6859
+ const input = await prompt("\u7C98\u8D34 API Key\uFF1A");
6860
+ if (input) {
6861
+ apiKey = input;
6862
+ break;
6863
+ }
6864
+ console.log("\u274C API Key \u4E0D\u80FD\u4E3A\u7A7A\uFF0C\u8BF7\u91CD\u8BD5");
6865
+ }
6866
+ if (!apiKey) {
6867
+ console.error("\n\u274C \u591A\u6B21\u8F93\u5165\u65E0\u6548\uFF0C\u8BF7\u91CD\u8BD5\u3002\n");
6868
+ process.exit(1);
6869
+ }
6870
+ writeSharedConfig({ apiKey });
6871
+ console.log(`
6872
+ \u2705 API Key \u5DF2\u4FDD\u5B58\uFF08${maskSecret(apiKey)}\uFF09`);
6873
+ printPostLoginReminderBanner();
6874
+ console.log("\u73B0\u5728\u53EF\u4EE5\u8FD0\u884C\uFF1A");
6875
+ console.log(" siluzan-tso list-accounts \u67E5\u770B\u5E7F\u544A\u8D26\u6237\u5217\u8868");
6876
+ console.log(" siluzan-tso balance -m Google \u67E5\u770B\u8D26\u6237\u4F59\u989D\n");
6877
+ } finally {
6878
+ rl.close();
6879
+ }
6880
+ }
6881
+ async function runInteractivePhoneLogin(menuOpts) {
6882
+ const rl = readline.createInterface({ input: process.stdin, output: process.stdout });
6883
+ const prompt = (q) => new Promise((res) => rl.question(q, (a) => res(a.trim())));
6884
+ try {
6885
+ const phoneRaw = await prompt("\n\u8BF7\u8F93\u5165\u624B\u673A\u53F7\uFF08\u5927\u9646\u53F7\u7801\uFF0C\u53EF\u5E26 +86\uFF09\uFF1A");
6886
+ const phone = validateAndNormalizePhone(phoneRaw);
6887
+ const tsoApiBase = process.env.SILUZAN_TSO_API_BASE ?? DEFAULT_API_BASE;
6888
+ const ssoBaseUrl = deriveSsoBaseUrl(tsoApiBase);
6889
+ console.log("\n\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550");
6890
+ console.log(" \u53D1\u9001\u767B\u5F55\u77ED\u4FE1\u9A8C\u8BC1\u7801");
6891
+ console.log("\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550");
6892
+ console.log(` \u624B\u673A\u53F7 : ${phone}`);
6893
+ console.log("\u2192 \u6B63\u5728\u5411\u624B\u673A\u53D1\u9001\u9A8C\u8BC1\u7801...");
6894
+ const sendRes = await sendPhoneLoginCode({ ssoBaseUrl, phone, verbose: menuOpts.verbose });
6895
+ if (!sendRes.ok) {
6896
+ console.error(`
6897
+ \u274C \u77ED\u4FE1\u9A8C\u8BC1\u7801\u53D1\u9001\u5931\u8D25\uFF1A${sendRes.message || "(\u540E\u7AEF\u672A\u8FD4\u56DE\u539F\u56E0)"}
6898
+ `);
6899
+ process.exit(1);
6900
+ }
6901
+ console.log("\u2713 \u9A8C\u8BC1\u7801\u5DF2\u53D1\u9001\uFF0810 \u5206\u949F\u5185\u6709\u6548\uFF09\u3002\n");
6902
+ const code = await prompt("\u8BF7\u8F93\u5165 6 \u4F4D\u9A8C\u8BC1\u7801\uFF1A");
6903
+ if (!code.trim()) {
6904
+ console.error("\n\u274C \u9A8C\u8BC1\u7801\u4E0D\u80FD\u4E3A\u7A7A\u3002\n");
6905
+ process.exit(1);
6906
+ }
6907
+ await executePhoneLoginWithVerifiedCode(phone, code.trim(), menuOpts);
6908
+ } finally {
6909
+ rl.close();
6910
+ }
6911
+ }
6912
+ async function runLoginMethodMenu(menuOpts) {
6913
+ const rl = readline.createInterface({ input: process.stdin, output: process.stdout });
6914
+ const prompt = (q) => new Promise((res) => rl.question(q, (a) => res(a.trim())));
6915
+ console.log("\n\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550");
6916
+ console.log(" Siluzan \u767B\u5F55");
6917
+ console.log("\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550");
6918
+ console.log("\n\u8BF7\u9009\u62E9\u767B\u5F55\u65B9\u5F0F\uFF1A");
6919
+ console.log(" 1) JWT Token\uFF08Bearer\uFF0C\u7C98\u8D34 access token\uFF09");
6920
+ console.log(" 2) API Key\uFF08\u7F51\u9875\u300C\u8BBE\u7F6E \u2192 API Key \u7BA1\u7406\u300D\u521B\u5EFA\u540E\u7C98\u8D34\uFF09");
6921
+ console.log(" 3) \u624B\u673A\u53F7 + \u77ED\u4FE1\u9A8C\u8BC1\u7801\uFF08\u5C06\u5411\u624B\u673A\u53D1\u9001\u9A8C\u8BC1\u7801\uFF0C\u9A8C\u8BC1\u540E\u81EA\u52A8\u521B\u5EFA API Key\uFF09");
6922
+ console.log(" 0) \u9000\u51FA\n");
6923
+ let choice = "";
6924
+ for (let attempt = 0; attempt < 5; attempt++) {
6925
+ choice = await prompt("\u8BF7\u8F93\u5165\u9009\u9879 [0-3]\uFF1A");
6926
+ if (choice === "0" || choice === "1" || choice === "2" || choice === "3") {
6777
6927
  break;
6778
6928
  }
6779
- console.log("\u274C API Key \u4E0D\u80FD\u4E3A\u7A7A\uFF0C\u8BF7\u91CD\u8BD5");
6929
+ console.log("\u65E0\u6548\u9009\u62E9\uFF0C\u8BF7\u8F93\u5165 0\u30011\u30012 \u6216 3\u3002");
6780
6930
  }
6781
6931
  rl.close();
6782
- if (!apiKey) {
6783
- console.error("\n\u274C \u591A\u6B21\u8F93\u5165\u65E0\u6548\uFF0C\u8BF7\u91CD\u8BD5\u3002\n");
6784
- process.exit(1);
6932
+ if (choice === "0" || choice === "") {
6933
+ console.log("\n\u5DF2\u53D6\u6D88\u3002\n");
6934
+ return;
6785
6935
  }
6786
- writeSharedConfig({ apiKey });
6787
- console.log(`
6788
- \u2705 API Key \u5DF2\u4FDD\u5B58\uFF08${maskSecret(apiKey)}\uFF09`);
6789
- console.log(` \u914D\u7F6E\u6587\u4EF6\uFF1A${CONFIG_FILE}`);
6790
- printPostLoginReminderBanner();
6791
- console.log("\u73B0\u5728\u53EF\u4EE5\u8FD0\u884C\uFF1A");
6792
- console.log(" siluzan-tso list-accounts \u67E5\u770B\u5E7F\u544A\u8D26\u6237\u5217\u8868");
6793
- console.log(" siluzan-tso balance -m Google \u67E5\u770B\u8D26\u6237\u4F59\u989D\n");
6936
+ if (choice === "1") {
6937
+ await runInteractiveJwtLogin();
6938
+ return;
6939
+ }
6940
+ if (choice === "2") {
6941
+ await runInteractiveApiKeyLogin();
6942
+ return;
6943
+ }
6944
+ await runInteractivePhoneLogin(menuOpts);
6945
+ }
6946
+ async function runLogin(opts = {}) {
6947
+ if (opts.apiKey !== void 0) {
6948
+ const key = opts.apiKey.trim();
6949
+ if (!key) {
6950
+ console.error("\n\u274C API Key \u4E0D\u80FD\u4E3A\u7A7A\u3002\n");
6951
+ process.exit(1);
6952
+ }
6953
+ writeSharedConfig({ apiKey: key });
6954
+ console.log(`
6955
+ \u2705 API Key \u5DF2\u4FDD\u5B58\uFF08${maskSecret(key)}\uFF09`);
6956
+ console.log(` \u914D\u7F6E\u6587\u4EF6\uFF1A${CONFIG_FILE}`);
6957
+ printPostLoginReminderBanner();
6958
+ console.log("\u73B0\u5728\u53EF\u4EE5\u8FD0\u884C siluzan-tso -h \u547D\u4EE4\u67E5\u770B\u5E2E\u52A9\u4E86\n");
6959
+ return;
6960
+ }
6961
+ if (opts.phone !== void 0) {
6962
+ await runPhoneLogin(opts);
6963
+ return;
6964
+ }
6965
+ if (opts.manual) {
6966
+ await runInteractiveJwtLogin();
6967
+ return;
6968
+ }
6969
+ await runLoginMethodMenu(opts);
6794
6970
  }
6795
6971
  function register(program2) {
6796
- program2.command("login").description("\u4FDD\u5B58\u8BA4\u8BC1\u51ED\u636E\u5230 ~/.siluzan/config.json\uFF08Token \u6216 API Key \u4E8C\u9009\u4E00\uFF09").option("--api-key <key>", "\u76F4\u63A5\u4FDD\u5B58 API Key\uFF08\u5728\u4E1D\u8DEF\u8D5E\u300C\u8BBE\u7F6E \u2192 API Key \u7BA1\u7406\u300D\u521B\u5EFA\uFF09\uFF0C\u63A8\u8350\u65B9\u5F0F").option("--manual", "\u4EA4\u4E92\u5F0F\u7C98\u8D34 JWT Token\uFF08\u65E0 API Key \u65F6\u4F7F\u7528\uFF09").action(async (opts) => {
6797
- await runLogin({ apiKey: opts.apiKey });
6972
+ program2.command("send-login-code").description("\u5411\u624B\u673A\u53F7\u53D1\u9001\u4E1D\u8DEF\u8D5E\u767B\u5F55\u77ED\u4FE1\u9A8C\u8BC1\u7801\uFF08\u65E0\u4EA4\u4E92\uFF0C\u9002\u5408 Agent\uFF1A\u5148\u53D1\u7801\u518D login --phone --code\uFF09").requiredOption("--phone <phone>", "\u4E2D\u56FD\u5927\u9646\u624B\u673A\u53F7\uFF08\u53EF\u5E26 +86\uFF09").option("--verbose", "\u8BE6\u7EC6\u9519\u8BEF\u4FE1\u606F", false).action(async (opts) => {
6973
+ await runSendLoginCode({ phone: opts.phone, verbose: opts.verbose });
6798
6974
  });
6975
+ program2.command("login").description(
6976
+ "\u4FDD\u5B58\u8BA4\u8BC1\u51ED\u636E\u5230 ~/.siluzan/config.json\uFF1B\u65E0\u53C2\u6570\u65F6\u5C55\u5F00 JWT / API Key / \u624B\u673A\u53F7\u4E09\u79CD\u4EA4\u4E92\u767B\u5F55"
6977
+ ).option("--api-key <key>", "\u76F4\u63A5\u4FDD\u5B58 API Key\uFF08\u7F51\u9875\u300C\u8BBE\u7F6E \u2192 API Key \u7BA1\u7406\u300D\u521B\u5EFA\uFF09").option("--phone <phone>", "\u624B\u673A\u53F7\uFF08\u987B\u4E0E --code \u540C\u4F20\uFF1B\u6216\u7EC8\u7AEF\u65E0\u53C2 login \u9009 3 \u7531 CLI \u53D1\u7801\uFF09").option("--code <code>", "\u77ED\u4FE1\u9A8C\u8BC1\u7801\uFF08\u987B\u4E0E --phone \u540C\u4F20\uFF09").option("--name <text>", "\u624B\u673A\u53F7\u767B\u5F55\u65F6\u81EA\u52A8\u521B\u5EFA\u7684 API Key \u540D\u79F0").option("--valid-days <n>", "API Key \u6709\u6548\u5929\u6570\uFF0C\u9ED8\u8BA4 90").option("--expires-at <iso>", "API Key \u7EDD\u5BF9\u8FC7\u671F\u65F6\u95F4 ISO8601\uFF08\u4E0E --valid-days \u4E8C\u9009\u4E00\uFF09").option("--services <csv>", "API Key \u670D\u52A1\uFF1ACSO,TSO,CUT \u9017\u53F7\u5206\u9694\uFF0C\u9ED8\u8BA4 TSO,CUT").option("--manual", "\u8DF3\u8FC7\u83DC\u5355\uFF0C\u76F4\u63A5\u7C98\u8D34 JWT\uFF08\u4E0E\u65E0\u53C2\u83DC\u5355\u9009\u9879 1 \u7B49\u4EF7\uFF09").option("--verbose", "\u8BE6\u7EC6 HTTP", false).action(
6978
+ async (opts) => {
6979
+ let validDays;
6980
+ if (opts.validDays !== void 0 && String(opts.validDays).trim() !== "") {
6981
+ const n = parseInt(String(opts.validDays), 10);
6982
+ if (Number.isNaN(n)) {
6983
+ console.error("\n\u274C --valid-days \u987B\u4E3A\u6574\u6570\n");
6984
+ process.exit(1);
6985
+ }
6986
+ validDays = n;
6987
+ }
6988
+ await runLogin({
6989
+ apiKey: opts.apiKey,
6990
+ phone: opts.phone,
6991
+ code: opts.code,
6992
+ name: opts.name,
6993
+ validDays,
6994
+ expiresAt: opts.expiresAt,
6995
+ services: opts.services,
6996
+ verbose: opts.verbose,
6997
+ manual: opts.manual
6998
+ });
6999
+ }
7000
+ );
6799
7001
  }
6800
7002
 
6801
7003
  // src/commands/config.ts
@@ -1,5 +1,5 @@
1
1
  {
2
2
  "slug": "siluzan-tso",
3
- "version": "1.1.18-beta.7",
4
- "publishedAt": 1778465751958
3
+ "version": "1.1.18-beta.8",
4
+ "publishedAt": 1778478403907
5
5
  }
@@ -57,7 +57,8 @@ siluzan-tso init -d /path/to-your/skills # 写入自定义目录
57
57
  ## 其它登录方式(TTY 交互 / 已有 API Key / JWT)
58
58
 
59
59
  ```bash
60
- siluzan-tso login # 交互式登录(需 TTY),按提示创建 API Key 后粘贴
60
+ siluzan-tso login # 无参:TTY 下展开菜单 → 1 JWT / 2 API Key / 3 手机号(发码+输码)
61
+ siluzan-tso login --manual # 跳过菜单,直接粘贴 JWT(会清除已存 API Key 以启用 Bearer)
61
62
  siluzan-tso login --api-key <YOUR_API_KEY> # 直接设置 API Key(跳过交互)
62
63
  siluzan-tso config set --api-key <Key> # 或 config 直接写入
63
64
  siluzan-tso config set --token <Token> # 备用:设置 JWT Token
@@ -94,7 +95,7 @@ siluzan-tso login --phone 13800138000 --code 123456 \
94
95
  >
95
96
  > **验证码错误/过期**:返回明确提示,让用户重新跑 `send-login-code` 拿新验证码。
96
97
  >
97
- > **AI 助手用法**:先调 `send-login-code` 发码、立即返回继续对话;等用户报出收到的验证码后,再调 `login --phone xxx --code xxx` 完成登录。**永远不要在没拿到 `--code` 的情况下调用 `login --phone xxx`,那会直接报错。**
98
+ > **AI 助手用法**:先调 `send-login-code` 发码、立即返回继续对话;等用户报出收到的验证码后,再调 `login --phone xxx --code xxx` 完成登录。**永远不要在没拿到 `--code` 的情况下调用 `login --phone xxx`,那会直接报错。**(人类在本地终端可直接运行无参 `siluzan-tso login` 选 3,由 CLI 发码并读验证码,无需先记 `send-login-code`。)
98
99
 
99
100
  ### 通过环境变量传入凭据(CI/CD 推荐)
100
101
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "siluzan-tso-cli",
3
- "version": "1.1.18-beta.7",
3
+ "version": "1.1.18-beta.8",
4
4
  "description": "Siluzan 广告账户管理 CLI — 查询账户、余额、消耗数据,管理绑定关系与充值。",
5
5
  "keywords": [
6
6
  "ad-account",