@zrhsh/wukong-cli 0.4.28 → 0.4.29

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/dist/cli.js CHANGED
@@ -801,6 +801,7 @@ init_esm_shims();
801
801
  var VersionCache = class {
802
802
  lastCheckTime = 0;
803
803
  cachedVersion = null;
804
+ cachedHighlights = [];
804
805
  checkInterval;
805
806
  /**
806
807
  * Constructor
@@ -824,17 +825,27 @@ var VersionCache = class {
824
825
  }
825
826
  /**
826
827
  * Set cached version with timestamp
828
+ *
829
+ * @param highlights 该版本的更新要点,缺省为空数组
827
830
  */
828
- set(version) {
831
+ set(version, highlights = []) {
829
832
  this.cachedVersion = version;
833
+ this.cachedHighlights = highlights;
830
834
  this.lastCheckTime = Date.now();
831
835
  }
836
+ /**
837
+ * Get cached highlights
838
+ */
839
+ getHighlights() {
840
+ return this.cachedHighlights;
841
+ }
832
842
  /**
833
843
  * Clear cache
834
844
  */
835
845
  clear() {
836
846
  this.lastCheckTime = 0;
837
847
  this.cachedVersion = null;
848
+ this.cachedHighlights = [];
838
849
  }
839
850
  /**
840
851
  * Get time since last check
@@ -853,6 +864,10 @@ var VersionCache = class {
853
864
  get version() {
854
865
  return this.cachedVersion;
855
866
  }
867
+ /** Cached highlights */
868
+ get highlights() {
869
+ return this.cachedHighlights;
870
+ }
856
871
  };
857
872
 
858
873
  // src/utils/version/compare.ts
@@ -897,23 +912,28 @@ var VersionChecker = class {
897
912
  return {
898
913
  hasUpdate: compareVersions(cached, this.currentVersion) > 0,
899
914
  currentVersion: this.currentVersion,
900
- latestVersion: cached
915
+ latestVersion: cached,
916
+ latestHighlights: this.cache.getHighlights()
901
917
  };
902
918
  }
903
919
  }
904
- const latestVersion = await this.provider.getLatestVersion();
905
- if (!latestVersion) {
920
+ const release = this.provider.getLatestRelease ? await this.provider.getLatestRelease() : await this.provider.getLatestVersion().then(
921
+ (version) => version ? { version, highlights: [] } : null
922
+ );
923
+ if (!release) {
906
924
  return {
907
925
  hasUpdate: false,
908
926
  currentVersion: this.currentVersion,
909
- latestVersion: null
927
+ latestVersion: null,
928
+ latestHighlights: []
910
929
  };
911
930
  }
912
- this.cache.set(latestVersion);
931
+ this.cache.set(release.version, release.highlights);
913
932
  return {
914
- hasUpdate: compareVersions(latestVersion, this.currentVersion) > 0,
933
+ hasUpdate: compareVersions(release.version, this.currentVersion) > 0,
915
934
  currentVersion: this.currentVersion,
916
- latestVersion
935
+ latestVersion: release.version,
936
+ latestHighlights: release.highlights
917
937
  };
918
938
  }
919
939
  /**
@@ -923,7 +943,7 @@ var VersionChecker = class {
923
943
  try {
924
944
  const result = await this.checkForUpdate();
925
945
  if (result.hasUpdate && notifier && result.latestVersion) {
926
- notifier.notify(result.currentVersion, result.latestVersion);
946
+ notifier.notify(result.currentVersion, result.latestVersion, result.latestHighlights);
927
947
  }
928
948
  } catch (error2) {
929
949
  }
@@ -933,6 +953,7 @@ var VersionChecker = class {
933
953
  */
934
954
  clearCache() {
935
955
  this.cache.clear();
956
+ this.provider.clearCache?.();
936
957
  }
937
958
  /**
938
959
  * Get current cache info
@@ -947,17 +968,37 @@ var VersionChecker = class {
947
968
 
948
969
  // src/utils/version/provider.ts
949
970
  init_esm_shims();
950
- var NpmVersionProvider = class {
971
+ function parseHighlights(value) {
972
+ if (!Array.isArray(value)) {
973
+ return [];
974
+ }
975
+ return value.filter((item) => typeof item === "string").map((item) => item.trim()).filter((item) => item.length > 0).slice(0, 5);
976
+ }
977
+ var NpmVersionProvider = class _NpmVersionProvider {
951
978
  constructor(packageName, registryUrl) {
952
979
  this.packageName = packageName;
953
980
  this.registryUrl = registryUrl;
954
981
  this.timeout = 5e3;
955
982
  }
956
983
  timeout;
984
+ cachedRelease = null;
985
+ cachedAt = 0;
986
+ /** 同一次进程内复用结果,避免 getLatestVersion / getLatestRelease 重复请求 */
987
+ static REUSE_MS = 6e4;
957
988
  /**
958
989
  * Get latest version from npm
959
990
  */
960
991
  async getLatestVersion() {
992
+ const release = await this.getLatestRelease();
993
+ return release?.version ?? null;
994
+ }
995
+ /**
996
+ * 获取最新版本及其更新摘要
997
+ */
998
+ async getLatestRelease() {
999
+ if (this.cachedRelease && Date.now() - this.cachedAt < _NpmVersionProvider.REUSE_MS) {
1000
+ return this.cachedRelease;
1001
+ }
961
1002
  try {
962
1003
  const controller = new AbortController();
963
1004
  const timeoutId = setTimeout(() => controller.abort(), this.timeout);
@@ -969,11 +1010,28 @@ var NpmVersionProvider = class {
969
1010
  return null;
970
1011
  }
971
1012
  const data = await response.json();
972
- return data["dist-tags"]?.latest || null;
1013
+ const version = data["dist-tags"]?.latest;
1014
+ if (!version) {
1015
+ return null;
1016
+ }
1017
+ const highlights = parseHighlights(
1018
+ data.versions?.[version]?.wukongCli?.highlights ?? data.wukongCli?.highlights
1019
+ );
1020
+ const release = { version, highlights };
1021
+ this.cachedRelease = release;
1022
+ this.cachedAt = Date.now();
1023
+ return release;
973
1024
  } catch (error2) {
974
1025
  return null;
975
1026
  }
976
1027
  }
1028
+ /**
1029
+ * 清除进程内复用缓存
1030
+ */
1031
+ clearCache() {
1032
+ this.cachedRelease = null;
1033
+ this.cachedAt = 0;
1034
+ }
977
1035
  };
978
1036
 
979
1037
  // src/utils/version/persistent-cache.ts
@@ -994,6 +1052,7 @@ var PersistentVersionCache = class extends VersionCache {
994
1052
  if (typeof data.lastCheckTime === "number" && typeof data.version === "string") {
995
1053
  this.lastCheckTime = data.lastCheckTime;
996
1054
  this.cachedVersion = data.version;
1055
+ this.cachedHighlights = Array.isArray(data.highlights) ? data.highlights.filter((item) => typeof item === "string") : [];
997
1056
  }
998
1057
  } catch {
999
1058
  }
@@ -1003,14 +1062,15 @@ var PersistentVersionCache = class extends VersionCache {
1003
1062
  mkdirSync(dirname(this.cacheFilePath), { recursive: true });
1004
1063
  const data = {
1005
1064
  lastCheckTime: this.lastCheckTime,
1006
- version: this.cachedVersion ?? ""
1065
+ version: this.cachedVersion ?? "",
1066
+ highlights: this.cachedHighlights
1007
1067
  };
1008
1068
  writeFileSync(this.cacheFilePath, JSON.stringify(data), "utf-8");
1009
1069
  } catch {
1010
1070
  }
1011
1071
  }
1012
- set(version) {
1013
- super.set(version);
1072
+ set(version, highlights = []) {
1073
+ super.set(version, highlights);
1014
1074
  this.saveToFile();
1015
1075
  }
1016
1076
  clear() {
@@ -1026,10 +1086,24 @@ import { join } from "path";
1026
1086
  // src/utils/version/notifier.ts
1027
1087
  init_esm_shims();
1028
1088
  import chalk2 from "chalk";
1089
+ var MAX_HIGHLIGHTS = 3;
1090
+ var SEPARATOR = "\u2500".repeat(58);
1029
1091
  var ConsoleNotifier = class {
1030
- notify(currentVersion, latestVersion) {
1031
- const message = `New version available: ${currentVersion} \u2192 ${latestVersion}, run 'wukong-cli update' to update.`;
1032
- console.log(chalk2.yellow(message));
1092
+ notify(currentVersion, latestVersion, highlights = []) {
1093
+ const lines = [
1094
+ SEPARATOR,
1095
+ `New version available: ${currentVersion} \u2192 ${latestVersion}`
1096
+ ];
1097
+ if (highlights.length > 0) {
1098
+ lines.push("\u672C\u6B21\u66F4\u65B0\uFF1A");
1099
+ for (const item of highlights.slice(0, MAX_HIGHLIGHTS)) {
1100
+ lines.push(` \u2022 ${item}`);
1101
+ }
1102
+ }
1103
+ lines.push("\u5347\u7EA7\uFF1Awukong-cli update");
1104
+ lines.push(" \u6216 npm i -g @zrhsh/wukong-cli@latest");
1105
+ lines.push(SEPARATOR);
1106
+ process.stderr.write(chalk2.yellow(lines.join("\n")) + "\n");
1033
1107
  }
1034
1108
  };
1035
1109
 
@@ -2242,6 +2316,7 @@ authCommands.command("status").description("Show authentication status").action(
2242
2316
  printEnvironmentInfo(env, envConfig.displayName);
2243
2317
  console.log(chalk5.dim(`Run: wukong-cli auth login`));
2244
2318
  console.log("");
2319
+ process.exitCode = 1;
2245
2320
  return;
2246
2321
  }
2247
2322
  try {
@@ -2266,6 +2341,7 @@ authCommands.command("status").description("Show authentication status").action(
2266
2341
  printEnvironmentInfo(env, envConfig.displayName);
2267
2342
  console.log(chalk5.dim(`Run: wukong-cli auth login`));
2268
2343
  console.log("");
2344
+ process.exitCode = 1;
2269
2345
  }
2270
2346
  } catch (error2) {
2271
2347
  if (error2 instanceof ConfigFileError) {
@@ -2273,6 +2349,7 @@ authCommands.command("status").description("Show authentication status").action(
2273
2349
  console.log("");
2274
2350
  console.log(chalk5.red(error2.toUserMessage()));
2275
2351
  console.log("");
2352
+ process.exitCode = 1;
2276
2353
  return;
2277
2354
  }
2278
2355
  console.log(chalk5.yellow(`\u2717 Not authenticated`));
@@ -2280,6 +2357,7 @@ authCommands.command("status").description("Show authentication status").action(
2280
2357
  printEnvironmentInfo(env, envConfig.displayName);
2281
2358
  console.log(chalk5.dim(`Run: wukong-cli auth login`));
2282
2359
  console.log("");
2360
+ process.exitCode = 1;
2283
2361
  }
2284
2362
  });
2285
2363
  authCommands.command("token").description("Display current token structure and JWT payload claims").action(async () => {
@@ -2477,8 +2555,37 @@ function detectPageQueryEnvelope(body) {
2477
2555
  };
2478
2556
  }
2479
2557
 
2558
+ // src/core/http/response-guard.ts
2559
+ init_esm_shims();
2560
+ var AUTH_ERROR_CODES = [401, 403, 9913, 9914];
2561
+ var LOGIN_HINT = "Run: wukong-cli auth login";
2562
+ function isAuthErrorCode(code) {
2563
+ return AUTH_ERROR_CODES.includes(code);
2564
+ }
2565
+ function withLoginHint(message) {
2566
+ if (message.includes(LOGIN_HINT)) {
2567
+ return message;
2568
+ }
2569
+ return `${message}
2570
+ Hint: \u767B\u5F55\u6001\u53EF\u80FD\u5DF2\u5931\u6548\uFF0C\u8BF7\u91CD\u65B0\u767B\u5F55\u3002${LOGIN_HINT}`;
2571
+ }
2572
+ function looksLikeHtml(value) {
2573
+ if (typeof value !== "string") {
2574
+ return false;
2575
+ }
2576
+ const head = value.trimStart().slice(0, 64).toLowerCase();
2577
+ return head.startsWith("<!doctype html") || head.startsWith("<html");
2578
+ }
2579
+ function describeHtmlResponse() {
2580
+ return [
2581
+ "\u63A5\u53E3\u8FD4\u56DE\u7684\u662F HTML \u9875\u9762\uFF0C\u4E0D\u662F JSON\u3002",
2582
+ "\u5E38\u89C1\u539F\u56E0\uFF1A\u767B\u5F55\u6001\u5931\u6548\u88AB\u91CD\u5B9A\u5411\u5230\u767B\u5F55\u9875\u3001\u63A5\u53E3\u8DEF\u5F84\u9519\u8BEF\u3001base URL \u6307\u5411\u4E86\u524D\u7AEF\u7AD9\u70B9\u3002",
2583
+ `\u8BF7\u5148\u6267\u884C wukong-cli auth status \u786E\u8BA4\u767B\u5F55\u6001\uFF0C\u5FC5\u8981\u65F6 ${LOGIN_HINT}`
2584
+ ].join("\n");
2585
+ }
2586
+
2480
2587
  // src/commands/http.ts
2481
- function fail(message, jsonMode, errorCode = null) {
2588
+ function fail(message, jsonMode, errorCode = null, exitCode = 1) {
2482
2589
  if (jsonMode) {
2483
2590
  process.stderr.write(
2484
2591
  JSON.stringify({ error: { code: errorCode, message } }, null, 2) + "\n"
@@ -2486,7 +2593,7 @@ function fail(message, jsonMode, errorCode = null) {
2486
2593
  } else {
2487
2594
  console.error(chalk6.red(message));
2488
2595
  }
2489
- process.exit(1);
2596
+ process.exit(exitCode);
2490
2597
  }
2491
2598
  function fixGitBashPath(url) {
2492
2599
  if (url.includes(":") && !url.startsWith("http")) {
@@ -2627,7 +2734,12 @@ function handleError(error2, jsonMode) {
2627
2734
  ${error2.toUserMessage()}`, jsonMode);
2628
2735
  }
2629
2736
  if (error2 instanceof ApiError) {
2630
- return fail(`API Error (code: ${error2.code}): ${error2.message}`, jsonMode, error2.code);
2737
+ const message = `API Error (code: ${error2.code}): ${error2.message}`;
2738
+ return fail(
2739
+ isAuthErrorCode(error2.code) ? withLoginHint(message) : message,
2740
+ jsonMode,
2741
+ error2.code
2742
+ );
2631
2743
  }
2632
2744
  return fail(error2 instanceof Error ? error2.message : String(error2), jsonMode);
2633
2745
  }
@@ -2658,6 +2770,10 @@ async function executeRequest(method, url, options) {
2658
2770
  };
2659
2771
  warnOnPageQueryEnvelope(context);
2660
2772
  const result = context.fullUrl.includes("/yst-") ? await sendWithYstToken(context) : await sendWithMainToken(context);
2773
+ if (looksLikeHtml(result.data)) {
2774
+ spinner.fail("Unexpected HTML response");
2775
+ fail(describeHtmlResponse(), jsonMode, null, 2);
2776
+ }
2661
2777
  spinner.succeed(`Response received (${result.duration}ms)`);
2662
2778
  renderResult(result, jsonMode);
2663
2779
  } catch (error2) {