siluzan-tso-cli 1.1.18-beta.1 → 1.1.18-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
@@ -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.1),供内部测试使用。正式发布后安装命令将改为 `npm install -g siluzan-tso-cli`。
54
+ > **注意**:当前为测试版(1.1.18-beta.2),供内部测试使用。正式发布后安装命令将改为 `npm install -g siluzan-tso-cli`。
55
55
 
56
56
  | 助手 | 建议 `--ai` |
57
57
  | ----------------------- | ------------------------------------ |
package/dist/index.js CHANGED
@@ -2913,23 +2913,15 @@ async function createApiKeyByBearer(opts) {
2913
2913
  }
2914
2914
  return data;
2915
2915
  }
2916
- async function runPhoneLoginAndIssueApiKey(opts) {
2917
- const sendResult = await sendPhoneLoginCode({
2918
- ssoBaseUrl: opts.ssoBaseUrl,
2919
- phone: opts.phone,
2920
- verbose: opts.verbose
2921
- });
2922
- if (!sendResult.ok) {
2923
- throw new Error(`\u77ED\u4FE1\u9A8C\u8BC1\u7801\u53D1\u9001\u5931\u8D25\uFF1A${sendResult.message || "(\u540E\u7AEF\u672A\u8FD4\u56DE\u539F\u56E0)"}`);
2924
- }
2925
- const code = await opts.getCode();
2916
+ async function issueApiKeyWithPhoneCode(opts) {
2917
+ const code = opts.code?.trim() ?? "";
2926
2918
  if (!code) {
2927
- throw new Error("\u5DF2\u53D6\u6D88\uFF08\u672A\u8F93\u5165\u9A8C\u8BC1\u7801\uFF09");
2919
+ throw new Error("\u9A8C\u8BC1\u7801\u4E0D\u80FD\u4E3A\u7A7A");
2928
2920
  }
2929
2921
  const tokenInfo = await loginByPhoneCode({
2930
2922
  ssoBaseUrl: opts.ssoBaseUrl,
2931
2923
  phone: opts.phone,
2932
- code: code.trim(),
2924
+ code,
2933
2925
  verbose: opts.verbose
2934
2926
  });
2935
2927
  const apiKey = await createApiKeyByBearer({
@@ -16447,15 +16439,53 @@ function defaultApiKeyName() {
16447
16439
  const today = (/* @__PURE__ */ new Date()).toISOString().slice(0, 10);
16448
16440
  return `CLI - ${host} - ${today}`;
16449
16441
  }
16450
- async function runPhoneLogin(opts) {
16451
- const rawPhone = opts.phone?.trim() ?? "";
16442
+ function validateAndNormalizePhone(rawInput) {
16443
+ const rawPhone = rawInput?.trim() ?? "";
16452
16444
  if (!isValidChinaPhone(rawPhone)) {
16453
16445
  console.error(
16454
16446
  "\n\u274C \u624B\u673A\u53F7\u683C\u5F0F\u9519\u8BEF\uFF1A\u4EC5\u652F\u6301\u4E2D\u56FD\u5927\u9646\u624B\u673A\u53F7\uFF0C\u53EF\u5E26\u6216\u4E0D\u5E26 +86\uFF08\u5982 13800138000 / +8613800138000\uFF09\u3002\n"
16455
16447
  );
16456
16448
  process.exit(1);
16457
16449
  }
16458
- const phone = normalizeChinaPhone(rawPhone);
16450
+ return normalizeChinaPhone(rawPhone);
16451
+ }
16452
+ async function runSendLoginCode(opts) {
16453
+ const phone = validateAndNormalizePhone(opts.phone);
16454
+ const tsoApiBase = process.env.SILUZAN_TSO_API_BASE ?? DEFAULT_API_BASE;
16455
+ const ssoBaseUrl = deriveSsoBaseUrl(tsoApiBase);
16456
+ 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");
16457
+ console.log(" Siluzan \u53D1\u9001\u767B\u5F55\u77ED\u4FE1\u9A8C\u8BC1\u7801");
16458
+ 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");
16459
+ console.log(` \u624B\u673A\u53F7 : ${phone}`);
16460
+ console.log(` SSO : ${ssoBaseUrl}
16461
+ `);
16462
+ console.log("\u2192 \u6B63\u5728\u5411\u624B\u673A\u53D1\u9001\u9A8C\u8BC1\u7801...");
16463
+ const r = await sendPhoneLoginCode({ ssoBaseUrl, phone, verbose: opts.verbose });
16464
+ if (!r.ok) {
16465
+ console.error(`
16466
+ \u274C \u77ED\u4FE1\u9A8C\u8BC1\u7801\u53D1\u9001\u5931\u8D25\uFF1A${r.message || "(\u540E\u7AEF\u672A\u8FD4\u56DE\u539F\u56E0)"}
16467
+ `);
16468
+ process.exit(1);
16469
+ }
16470
+ console.log(`\u2713 \u9A8C\u8BC1\u7801\u5DF2\u53D1\u9001\uFF0810 \u5206\u949F\u5185\u6709\u6548\uFF09\u3002
16471
+ `);
16472
+ 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");
16473
+ console.log(` siluzan-tso login --phone ${phone} --code <6\u4F4D\u9A8C\u8BC1\u7801>
16474
+ `);
16475
+ console.log("\u53EF\u9009\u53C2\u6570\uFF1A--name / --valid-days / --expires-at / --services");
16476
+ console.log("\uFF08\u9ED8\u8BA4\u521B\u5EFA 90 \u5929\u6709\u6548\u3001\u52FE\u9009 TSO + CUT \u670D\u52A1\u7684 API Key\uFF09\n");
16477
+ }
16478
+ async function runPhoneLogin(opts) {
16479
+ const phone = validateAndNormalizePhone(opts.phone);
16480
+ const code = opts.code?.trim() ?? "";
16481
+ if (!code) {
16482
+ 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");
16483
+ console.error(` 1) siluzan-tso send-login-code --phone ${phone}`);
16484
+ console.error(` 2) siluzan-tso login --phone ${phone} --code <\u6536\u5230\u76846\u4F4D\u9A8C\u8BC1\u7801>
16485
+ `);
16486
+ 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");
16487
+ process.exit(1);
16488
+ }
16459
16489
  let allowedServices;
16460
16490
  try {
16461
16491
  allowedServices = parseAllowedServices(opts.services);
@@ -16472,10 +16502,12 @@ async function runPhoneLogin(opts) {
16472
16502
  const tsoApiBase = process.env.SILUZAN_TSO_API_BASE ?? DEFAULT_API_BASE;
16473
16503
  const ssoBaseUrl = deriveSsoBaseUrl(tsoApiBase);
16474
16504
  const csoBaseUrl = deriveCsoApiBaseUrl(tsoApiBase);
16505
+ const apiKeyName = opts.name ?? defaultApiKeyName();
16475
16506
  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");
16476
16507
  console.log(" Siluzan \u767B\u5F55\uFF08\u624B\u673A\u53F7 + \u77ED\u4FE1\u9A8C\u8BC1\u7801\uFF09");
16477
16508
  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");
16478
16509
  console.log(` \u624B\u673A\u53F7 : ${phone}`);
16510
+ console.log(` \u9A8C\u8BC1\u7801 : ${code}`);
16479
16511
  console.log(` SSO : ${ssoBaseUrl}`);
16480
16512
  console.log(` CSO API : ${csoBaseUrl}`);
16481
16513
  console.log(` \u670D\u52A1\u8303\u56F4 : ${allowedServices.join(", ")}`);
@@ -16484,40 +16516,20 @@ async function runPhoneLogin(opts) {
16484
16516
  } else {
16485
16517
  console.log(` \u6709\u6548\u671F : ${opts.validDays ?? 90} \u5929`);
16486
16518
  }
16487
- const apiKeyName = opts.name ?? defaultApiKeyName();
16488
16519
  console.log(` API Key \u540D\u79F0 : ${apiKeyName}
16489
16520
  `);
16490
- console.log("\u2192 \u6B63\u5728\u5411\u624B\u673A\u53D1\u9001\u9A8C\u8BC1\u7801...");
16521
+ console.log("\u2192 \u6B63\u5728\u6821\u9A8C\u9A8C\u8BC1\u7801\u5E76\u521B\u5EFA API Key...");
16491
16522
  try {
16492
- const created = await runPhoneLoginAndIssueApiKey({
16523
+ const created = await issueApiKeyWithPhoneCode({
16493
16524
  ssoBaseUrl,
16494
16525
  csoBaseUrl,
16495
16526
  phone,
16527
+ code,
16496
16528
  apiKeyName,
16497
16529
  validDays: opts.expiresAt ? void 0 : opts.validDays ?? 90,
16498
16530
  expiresAt: opts.expiresAt,
16499
16531
  allowedServices,
16500
- verbose: opts.verbose,
16501
- getCode: async () => {
16502
- if (opts.code !== void 0) {
16503
- const trimmed = opts.code.trim();
16504
- if (!trimmed) {
16505
- console.error("\u274C --code \u4E0D\u80FD\u4E3A\u7A7A\u3002");
16506
- return null;
16507
- }
16508
- console.log(`\u2713 \u9A8C\u8BC1\u7801\u5DF2\u53D1\u9001\uFF0C\u4F7F\u7528\u547D\u4EE4\u884C\u4F20\u5165\u7684 code\uFF08${trimmed}\uFF09\u3002`);
16509
- return trimmed;
16510
- }
16511
- console.log("\u2713 \u9A8C\u8BC1\u7801\u5DF2\u53D1\u9001\uFF0810 \u5206\u949F\u5185\u6709\u6548\uFF09\u3002");
16512
- const rl = readline2.createInterface({ input: process.stdin, output: process.stdout });
16513
- const input = await new Promise(
16514
- (res) => rl.question("\u8BF7\u8F93\u5165\u6536\u5230\u7684 6 \u4F4D\u9A8C\u8BC1\u7801\uFF1A", (a) => {
16515
- rl.close();
16516
- res(a.trim());
16517
- })
16518
- );
16519
- return input || null;
16520
- }
16532
+ verbose: opts.verbose
16521
16533
  });
16522
16534
  writeSharedConfig({ apiKey: created.rawKey });
16523
16535
  console.log("\n\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500");
@@ -16545,7 +16557,16 @@ async function runPhoneLogin(opts) {
16545
16557
  `
16546
16558
  \u8BE5\u624B\u673A\u53F7\u5C1A\u672A\u6CE8\u518C\u4E1D\u8DEF\u8D5E\u8D26\u53F7\u3002\u8BF7\u5148\u5728\u7F51\u9875\u7AEF\u6CE8\u518C\uFF1A
16547
16559
  ${WEB_BASE_URL}
16548
- \u6CE8\u518C\u6210\u529F\u540E\u518D\u56DE\u5230 CLI \u91CD\u8BD5 siluzan-tso login --phone ${phone}
16560
+ \u6CE8\u518C\u6210\u529F\u540E\u518D\u56DE\u5230 CLI \u91CD\u8BD5\u4E24\u6BB5\u5F0F\u767B\u5F55\uFF1A
16561
+ siluzan-tso send-login-code --phone ${phone}
16562
+ siluzan-tso login --phone ${phone} --code <6\u4F4D\u9A8C\u8BC1\u7801>
16563
+ `
16564
+ );
16565
+ } else if (/验证码错误|验证码/.test(msg)) {
16566
+ console.error(
16567
+ `
16568
+ \u9A8C\u8BC1\u7801\u53EF\u80FD\u5DF2\u8FC7\u671F\u6216\u8F93\u9519\u3002\u8BF7\u91CD\u65B0\u53D1\u7801\u540E\u518D\u8BD5\uFF1A
16569
+ siluzan-tso send-login-code --phone ${phone}
16549
16570
  `
16550
16571
  );
16551
16572
  }
@@ -16664,15 +16685,22 @@ program.hook("preAction", async () => {
16664
16685
  setWriteAuditCliCommit(extractCommitFromArgv(process.argv.slice(2)));
16665
16686
  setCliInvocationForAudit(process.argv.slice(2));
16666
16687
  const activeCmd = process.argv[2];
16667
- if (activeCmd !== "update" && activeCmd !== "login" && activeCmd !== "audit") {
16688
+ if (activeCmd !== "update" && activeCmd !== "login" && activeCmd !== "send-login-code" && activeCmd !== "audit") {
16668
16689
  await notifyIfOutdated().catch(() => {
16669
16690
  });
16670
16691
  }
16671
16692
  });
16672
- program.command("login").description("\u4FDD\u5B58\u8BA4\u8BC1\u51ED\u636E\u5230 ~/.siluzan/config.json\uFF08API Key / \u624B\u673A\u53F7\u9A8C\u8BC1\u7801 / \u4EA4\u4E92\u5F0F\u4E09\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\u6700\u9AD8\u4F18\u5148\u7EA7").option(
16693
+ program.command("send-login-code").description(
16694
+ "\u5411\u624B\u673A\u53D1\u9001 SSO_LOGIN \u77ED\u4FE1\u9A8C\u8BC1\u7801\uFF08\u624B\u673A\u53F7\u767B\u5F55\u7684\u7B2C\u4E00\u6B65\uFF09\uFF1B\u7ACB\u5373\u8FD4\u56DE\uFF0C\u65E0\u4EFB\u4F55\u4EA4\u4E92\u3002\n \u6536\u5230\u9A8C\u8BC1\u7801\u540E\u7528 `siluzan-tso login --phone xxx --code xxx` \u5B8C\u6210\u767B\u5F55\u3002"
16695
+ ).requiredOption(
16673
16696
  "--phone <phone>",
16674
- "11 \u4F4D\u624B\u673A\u53F7\uFF1B\u89E6\u53D1\u77ED\u4FE1\u9A8C\u8BC1\u7801\u767B\u5F55\u5E76\u81EA\u52A8\u7B7E\u53D1 API Key\uFF08\u9700\u624B\u673A\u53F7\u5DF2\u5728\u4E1D\u8DEF\u8D5E\u6CE8\u518C\uFF09"
16675
- ).option("--code <code>", "\u77ED\u4FE1\u9A8C\u8BC1\u7801\uFF1B\u4E0E --phone \u914D\u5408\u4F7F\u7528\uFF0C\u8DF3\u8FC7\u4EA4\u4E92\u5F0F\u8F93\u5165").option("--name <name>", "\u81EA\u52A8\u521B\u5EFA\u7684 API Key \u540D\u79F0\uFF0C\u9ED8\u8BA4 `CLI - <hostname> - <yyyy-MM-dd>`").option("--valid-days <days>", "API Key \u6709\u6548\u671F\uFF08\u5929\uFF09\uFF0C\u9ED8\u8BA4 90", (v) => parseInt(v, 10)).option("--expires-at <iso8601>", "API Key \u7EDD\u5BF9\u8FC7\u671F\u65F6\u95F4\uFF08ISO 8601\uFF09\uFF0C\u4E0E --valid-days \u4E8C\u9009\u4E00").option(
16697
+ "\u4E2D\u56FD\u5927\u9646\u624B\u673A\u53F7\uFF0C\u53EF\u5E26\u6216\u4E0D\u5E26 +86\uFF08\u5982 13800138000 / +8613800138000\uFF09"
16698
+ ).option("--verbose", "\u8F93\u51FA\u6BCF\u6B21 HTTP \u8BF7\u6C42 URL\uFF08\u8C03\u8BD5\u7528\uFF09").action(async (opts) => {
16699
+ await runSendLoginCode({ phone: opts.phone, verbose: opts.verbose });
16700
+ });
16701
+ program.command("login").description(
16702
+ "\u4FDD\u5B58\u8BA4\u8BC1\u51ED\u636E\u5230 ~/.siluzan/config.json\uFF1A\n \xB7 --api-key <key> \u76F4\u63A5\u4FDD\u5B58 API Key\uFF08CI/CD \u63A8\u8350\uFF09\n \xB7 --phone <\u624B\u673A\u53F7> --code <\u9A8C\u8BC1\u7801> \u4E24\u6BB5\u5F0F\u767B\u5F55\u7B2C\u4E8C\u6B65\uFF08\u5148\u8C03 send-login-code\uFF09\n \xB7 \u65E0\u53C2\u6570 \u4EA4\u4E92\u5F0F\u5F15\u5BFC\u7528\u6237\u53BB\u7F51\u9875\u7C98\u8D34 API Key"
16703
+ ).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\u6700\u9AD8\u4F18\u5148\u7EA7").option("--phone <phone>", "\u4E2D\u56FD\u5927\u9646\u624B\u673A\u53F7\uFF0C\u9700\u914D\u5408 --code \u4E00\u8D77\u4F7F\u7528\uFF08\u5FC5\u987B\u5148\u8C03 send-login-code \u53D1\u7801\uFF09").option("--code <code>", "\u77ED\u4FE1\u9A8C\u8BC1\u7801\uFF086 \u4F4D\uFF09\uFF1B\u4E0E --phone \u4E00\u8D77\u5FC5\u586B").option("--name <name>", "\u81EA\u52A8\u521B\u5EFA\u7684 API Key \u540D\u79F0\uFF0C\u9ED8\u8BA4 `CLI - <hostname> - <yyyy-MM-dd>`").option("--valid-days <days>", "API Key \u6709\u6548\u671F\uFF08\u5929\uFF09\uFF0C\u9ED8\u8BA4 90", (v) => parseInt(v, 10)).option("--expires-at <iso8601>", "API Key \u7EDD\u5BF9\u8FC7\u671F\u65F6\u95F4\uFF08ISO 8601\uFF09\uFF0C\u4E0E --valid-days \u4E8C\u9009\u4E00").option(
16676
16704
  "--services <list>",
16677
16705
  "API Key \u53EF\u8BBF\u95EE\u7684\u670D\u52A1\u5217\u8868\uFF0C\u9017\u53F7\u5206\u9694\uFF1B\u53EF\u9009 CSO/TSO/CUT\uFF0C\u9ED8\u8BA4 `TSO,CUT`"
16678
16706
  ).option("--manual", "\u4EA4\u4E92\u5F0F\u7C98\u8D34 JWT Token\uFF08\u65E0 API Key \u65F6\u4F7F\u7528\uFF0C\u5DF2\u5E9F\u5F03\uFF09").option("--verbose", "\u8F93\u51FA\u6BCF\u6B21 HTTP \u8BF7\u6C42 URL\uFF08\u8C03\u8BD5\u7528\uFF09").action(
@@ -1,5 +1,5 @@
1
1
  {
2
2
  "slug": "siluzan-tso",
3
- "version": "1.1.18-beta.1",
4
- "publishedAt": 1778315223329
3
+ "version": "1.1.18-beta.2",
4
+ "publishedAt": 1778317442558
5
5
  }
@@ -43,7 +43,8 @@ siluzan-tso init -d /path/to-your/skills # 写入自定义目录
43
43
  ```bash
44
44
  siluzan-tso login # 交互式登录,按提示创建 API Key 后粘贴
45
45
  siluzan-tso login --api-key <YOUR_API_KEY> # 直接设置 API Key(跳过交互)
46
- siluzan-tso login --phone 138xxxx # 手机号 + 短信验证码登录(自动签发并保存 API Key)
46
+ siluzan-tso send-login-code --phone 138xxxx # 两段式登录第 1 步:发送短信验证码
47
+ siluzan-tso login --phone 138xxxx --code 123456 # 两段式登录第 2 步:用验证码完成登录
47
48
  siluzan-tso config set --api-key <Key> # 或通过 config set 直接写入
48
49
  siluzan-tso config set --token <Token> # 备用:设置 JWT Token
49
50
  ```
@@ -52,34 +53,43 @@ API Key 获取入口:`https://www-ci.siluzan.com/v3/foreign_trade/settings/api
52
53
 
53
54
  ### 通过手机号 + 验证码登录(对话式 AI 推荐)
54
55
 
55
- 适合让 AI 助手在对话里直接帮用户完成登录:用户报手机号 AI 调 `siluzan-tso login --phone <号码>` → 用户报到的验证码 → AI 用 `--phone <号码> --code <验证码>` 一次性完成签发;签发成功的 API Key 会自动写入 `~/.siluzan/config.json`,后续命令直接复用。
56
+ **两段式调用**,专为 AI Agent 设计——任何一步都不会进入交互等待,绝不会卡住 stdout。
57
+ 拆分后单一职责:第 1 步只发码;第 2 步只用 code 换 API Key。这样 Agent 不会因为"看到 stdout 卡住就重试"而触发短信轰炸。
58
+
59
+ | 步骤 | 命令 | 说明 |
60
+ | ---- | ---- | ---- |
61
+ | 1 | `siluzan-tso send-login-code --phone <手机号>` | 仅向手机发送 6 位验证码,立即返回;**绝不创建 API Key** |
62
+ | 2 | `siluzan-tso login --phone <手机号> --code <验证码>` | 用 code 完成登录并自动签发 API Key 写入 `~/.siluzan/config.json` |
56
63
 
57
64
  ```bash
58
- # 第 1 次调用:发送验证码(命令会进入交互式等输入验证码)
59
- siluzan-tso login --phone 13800138000
65
+ # 第 1 步:让用户报出手机号后,立刻发码(命令立即返回,不会等待输入)
66
+ siluzan-tso send-login-code --phone 13800138000
60
67
 
61
- # 第 2 次调用:用户告诉 AI 收到的验证码后,一次性把 phone+code 都传进去(AI 推荐用法)
68
+ # 第 2 步:让用户读出收到的 6 位验证码,再调登录命令
62
69
  siluzan-tso login --phone 13800138000 --code 123456
63
70
 
64
- # 自定义自动创建的 API Key 名称、有效期、可访问的服务
71
+ # 自定义 API Key 名称 / 有效期 / 服务范围
65
72
  siluzan-tso login --phone 13800138000 --code 123456 \
66
73
  --name "Cursor - my-mac - 2026" \
67
74
  --valid-days 30 \
68
75
  --services TSO,CUT
69
76
  ```
70
77
 
71
- | 参数 | 说明 | 默认值 |
72
- | ---------------- | --------------------------------------------------------------------------------- | ----------------------------------- |
73
- | `--phone` | 11 位中国大陆手机号;触发短信验证码流程;**手机号必须已在丝路赞网页端注册** | 必填 |
74
- | `--code` | 6 位短信验证码;不传则进入交互式提示输入 | 交互式输入 |
75
- | `--name` | 自动创建的 API Key 显示名称 | `CLI - <hostname> - <yyyy-MM-dd>` |
76
- | `--valid-days` | API Key 有效期(天),与 `--expires-at` 二选一 | `90` |
77
- | `--expires-at` | API Key 绝对过期时间(ISO 8601) | 不传则用 `--valid-days` |
78
- | `--services` | 可访问的服务列表,逗号分隔;可选 `CSO`/`TSO`/`CUT` | `TSO,CUT`(广告投放 + 素材中心) |
79
- | `--verbose` | 输出每次 HTTP 请求的 URL,便于排错 | 关闭 |
80
-
81
- > **未注册手机号** 会返回 `❌ 登录失败:手机未注册` 并附带网页注册地址,引导用户先去网页注册再回来重试。
82
- > **AI 助手用法**:第一次只把 `--phone` 传进去触发发码;用户在对话里报了验证码后,**重新调用并同时传 `--phone` + `--code`**,避免被交互式 prompt 卡住。
78
+ | 参数 | 命令 | 说明 | 默认值 |
79
+ | ---- | ---- | ---- | ---- |
80
+ | `--phone` | 两个命令都需要 | 中国大陆手机号,可带或不带 +86(如 `13800138000` / `+8613800138000`);底层会自动补 `+86` 前缀;**手机号必须已在丝路赞网页端注册** | 必填 |
81
+ | `--code` | 仅 `login` | 6 位短信验证码(来自第 1 步发码后的短信);**login 命令必填**,未传会直接报错指引重新走两段式 | 必填(login 命令) |
82
+ | `--name` | 仅 `login` | 自动创建的 API Key 显示名称 | `CLI - <hostname> - <yyyy-MM-dd>` |
83
+ | `--valid-days` | 仅 `login` | API Key 有效期(天),与 `--expires-at` 二选一 | `90` |
84
+ | `--expires-at` | 仅 `login` | API Key 绝对过期时间(ISO 8601) | 不传则用 `--valid-days` |
85
+ | `--services` | 仅 `login` | 可访问的服务列表,逗号分隔;可选 `CSO`/`TSO`/`CUT` | `TSO,CUT`(广告投放 + 素材中心) |
86
+ | `--verbose` | 两个命令都支持 | 输出每次 HTTP 请求的 URL,便于排错 | 关闭 |
87
+
88
+ > **未注册手机号**:`login` 2 步会返回 `❌ 登录失败:手机未注册` 并附带网页注册地址,引导用户先去网页注册再回来重试两段式。
89
+ >
90
+ > **验证码错误/过期**:返回明确提示,让用户重新跑 `send-login-code` 拿新验证码。
91
+ >
92
+ > **AI 助手用法**:先调 `send-login-code` 发码、立即返回继续对话;等用户报出收到的验证码后,再调 `login --phone xxx --code xxx` 完成登录。**永远不要在没拿到 `--code` 的情况下调用 `login --phone xxx`,那会直接报错。**
83
93
 
84
94
  ### 通过环境变量传入凭据(CI/CD 推荐)
85
95
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "siluzan-tso-cli",
3
- "version": "1.1.18-beta.1",
3
+ "version": "1.1.18-beta.2",
4
4
  "description": "Siluzan 广告账户管理 CLI — 查询账户、余额、消耗数据,管理绑定关系与充值。",
5
5
  "keywords": [
6
6
  "ad-account",