koishi-plugin-aka-ai-generator 0.5.2 → 0.5.4

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/lib/index.js CHANGED
@@ -42,10 +42,14 @@ function sanitizeError(error) {
42
42
  const sanitized = {};
43
43
  for (const key in error) {
44
44
  const lowerKey = key.toLowerCase();
45
- if (lowerKey.includes("apikey") || lowerKey.includes("api_key") || lowerKey.includes("apikey") || lowerKey === "key" || lowerKey === "authorization" || lowerKey === "token" || lowerKey === "secret" || lowerKey === "password") {
45
+ if (lowerKey.includes("apikey") || lowerKey.includes("api_key") || lowerKey === "key" || lowerKey === "authorization" || lowerKey === "token" || lowerKey === "secret" || lowerKey === "password" || lowerKey === "x-goog-api-key") {
46
46
  sanitized[key] = "[REDACTED]";
47
47
  continue;
48
48
  }
49
+ if (lowerKey === "url" && typeof error[key] === "string") {
50
+ sanitized[key] = sanitizeUrl(error[key]);
51
+ continue;
52
+ }
49
53
  sanitized[key] = sanitizeError(error[key]);
50
54
  }
51
55
  return sanitized;
@@ -55,9 +59,26 @@ function sanitizeError(error) {
55
59
  __name(sanitizeError, "sanitizeError");
56
60
  function sanitizeString(str) {
57
61
  if (typeof str !== "string") return str;
58
- return str.replace(/key["\s:=]+([a-zA-Z0-9_-]{20,})/gi, 'key="[REDACTED]"').replace(/apikey["\s:=]+([a-zA-Z0-9_-]{20,})/gi, 'apikey="[REDACTED]"').replace(/api_key["\s:=]+([a-zA-Z0-9_-]{20,})/gi, 'api_key="[REDACTED]"').replace(/authorization["\s:=]+(Bearer\s+)?([a-zA-Z0-9_-]{20,})/gi, 'authorization="[REDACTED]"').replace(/Bearer\s+([a-zA-Z0-9_-]{20,})/gi, "Bearer [REDACTED]");
62
+ let sanitized = str.replace(/[?&]key=[^&\s"']+/gi, "?key=[REDACTED]").replace(/[?&]apikey=[^&\s"']+/gi, "&apikey=[REDACTED]").replace(/[?&]api_key=[^&\s"']+/gi, "&api_key=[REDACTED]");
63
+ sanitized = sanitized.replace(/key["\s:=]+([a-zA-Z0-9_-]{20,})/gi, 'key="[REDACTED]"').replace(/apikey["\s:=]+([a-zA-Z0-9_-]{20,})/gi, 'apikey="[REDACTED]"').replace(/api_key["\s:=]+([a-zA-Z0-9_-]{20,})/gi, 'api_key="[REDACTED]"').replace(/authorization["\s:=]+(Bearer\s+)?([a-zA-Z0-9_-]{20,})/gi, 'authorization="[REDACTED]"').replace(/Bearer\s+([a-zA-Z0-9_-]{20,})/gi, "Bearer [REDACTED]").replace(/x-goog-api-key["\s:]+([a-zA-Z0-9_-]{20,})/gi, "x-goog-api-key: [REDACTED]").replace(/X-Goog-Api-Key["\s:]+([a-zA-Z0-9_-]{20,})/gi, "X-Goog-Api-Key: [REDACTED]");
64
+ return sanitized;
59
65
  }
60
66
  __name(sanitizeString, "sanitizeString");
67
+ function sanitizeUrl(url) {
68
+ if (typeof url !== "string") return url;
69
+ try {
70
+ const urlObj = new URL(url);
71
+ urlObj.searchParams.delete("key");
72
+ urlObj.searchParams.delete("apikey");
73
+ urlObj.searchParams.delete("api_key");
74
+ urlObj.searchParams.delete("token");
75
+ urlObj.searchParams.delete("secret");
76
+ return urlObj.toString();
77
+ } catch (e) {
78
+ return sanitizeString(url);
79
+ }
80
+ }
81
+ __name(sanitizeUrl, "sanitizeUrl");
61
82
 
62
83
  // src/providers/yunwu.ts
63
84
  async function downloadImageAsBase64(ctx, url, timeout, logger) {
@@ -752,6 +773,9 @@ var GeminiProvider = class {
752
773
  this.config = config;
753
774
  }
754
775
  async generateImages(prompt, imageUrls, numImages) {
776
+ if (!this.config.apiKey || !this.config.apiKey.trim()) {
777
+ throw new Error("Gemini API key 未配置或为空");
778
+ }
755
779
  let urls = [];
756
780
  if (Array.isArray(imageUrls)) {
757
781
  urls = imageUrls.filter((url) => url && typeof url === "string" && url.trim());
@@ -784,7 +808,6 @@ var GeminiProvider = class {
784
808
  const requestData = {
785
809
  contents: [
786
810
  {
787
- role: "user",
788
811
  parts: [
789
812
  { text: prompt },
790
813
  ...imageParts
@@ -794,20 +817,30 @@ var GeminiProvider = class {
794
817
  generationConfig: {
795
818
  responseModalities: ["IMAGE"],
796
819
  imageConfig: {
797
- aspectRatio: "16:9",
798
- imageSize: "4K"
820
+ aspectRatio: "16:9"
799
821
  }
800
822
  }
801
823
  };
802
- logger.debug("调用 Gemini API", { prompt, imageCount: urls.length, numImages, current: i + 1, endpoint });
824
+ logger.debug("调用 Gemini API", {
825
+ prompt: prompt.substring(0, 100),
826
+ imageCount: urls.length,
827
+ numImages,
828
+ current: i + 1,
829
+ endpoint,
830
+ hasApiKey: !!this.config.apiKey,
831
+ apiKeyLength: this.config.apiKey?.length || 0,
832
+ modelId: this.config.modelId
833
+ });
803
834
  try {
804
835
  const response = await ctx.http.post(
805
836
  endpoint,
806
837
  requestData,
807
838
  {
808
839
  headers: {
809
- "Content-Type": "application/json",
810
- "x-goog-api-key": this.config.apiKey
840
+ "Content-Type": "application/json"
841
+ },
842
+ params: {
843
+ key: this.config.apiKey
811
844
  },
812
845
  timeout: this.config.apiTimeout * 1e3
813
846
  }
@@ -827,14 +860,33 @@ var GeminiProvider = class {
827
860
  } catch (error) {
828
861
  const sanitizedError = sanitizeError(error);
829
862
  const safeMessage = typeof error?.message === "string" ? sanitizeString(error.message) : "未知错误";
830
- logger.error("Gemini API 调用失败", {
863
+ const errorDetails = {
831
864
  message: safeMessage,
832
865
  code: error?.code,
833
866
  status: error?.response?.status,
834
- responseData: error?.response?.data ? sanitizeString(JSON.stringify(error.response.data).substring(0, 500)) : void 0,
867
+ statusText: error?.response?.statusText,
835
868
  current: i + 1,
836
- total: numImages
837
- });
869
+ total: numImages,
870
+ errorType: error?.name || error?.constructor?.name || "Unknown"
871
+ };
872
+ if (error?.response?.data) {
873
+ try {
874
+ const responseStr = JSON.stringify(error.response.data);
875
+ errorDetails.responseData = sanitizeString(responseStr.substring(0, 1e3));
876
+ } catch (e) {
877
+ errorDetails.responseData = "无法序列化响应数据";
878
+ }
879
+ }
880
+ if (error?.config) {
881
+ errorDetails.url = error.config.url ? sanitizeUrl(error.config.url) : void 0;
882
+ errorDetails.method = error.config.method;
883
+ errorDetails.hasData = !!error.config.data;
884
+ }
885
+ if (error?.code === "ECONNREFUSED" || error?.code === "ETIMEDOUT" || error?.code === "ENOTFOUND") {
886
+ errorDetails.networkError = true;
887
+ errorDetails.networkErrorCode = error.code;
888
+ }
889
+ logger.error("Gemini API 调用失败", errorDetails);
838
890
  if (allImages.length > 0) {
839
891
  logger.warn("部分图片生成失败,返回已生成的图片", { generated: allImages.length, requested: numImages });
840
892
  break;
@@ -22,3 +22,7 @@ export declare function sanitizeError(error: any): any;
22
22
  * 清理字符串中的 API KEY 模式
23
23
  */
24
24
  export declare function sanitizeString(str: string): string;
25
+ /**
26
+ * 清理 URL 中的敏感查询参数
27
+ */
28
+ export declare function sanitizeUrl(url: string): string;
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "koishi-plugin-aka-ai-generator",
3
3
  "description": "自用AI生成插件(GPTGod & Yunwu)",
4
- "version": "0.5.2",
4
+ "version": "0.5.4",
5
5
  "main": "lib/index.js",
6
6
  "typings": "lib/index.d.ts",
7
7
  "files": [