@hirohsu/user-web-feedback 2.8.15 → 2.8.17

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.
Files changed (3) hide show
  1. package/dist/cli.cjs +482 -33
  2. package/dist/index.cjs +479 -30
  3. package/package.json +1 -1
package/dist/cli.cjs CHANGED
@@ -2871,7 +2871,7 @@ var require_main = __commonJS({
2871
2871
  init_cjs_shims();
2872
2872
  var fs9 = require("fs");
2873
2873
  var path10 = require("path");
2874
- var os4 = require("os");
2874
+ var os7 = require("os");
2875
2875
  var crypto5 = require("crypto");
2876
2876
  var packageJson = require_package();
2877
2877
  var version2 = packageJson.version;
@@ -2994,7 +2994,7 @@ var require_main = __commonJS({
2994
2994
  return null;
2995
2995
  }
2996
2996
  function _resolveHome(envPath) {
2997
- return envPath[0] === "~" ? path10.join(os4.homedir(), envPath.slice(1)) : envPath;
2997
+ return envPath[0] === "~" ? path10.join(os7.homedir(), envPath.slice(1)) : envPath;
2998
2998
  }
2999
2999
  function _configVault(options) {
3000
3000
  const debug = Boolean(options && options.debug);
@@ -3159,7 +3159,9 @@ var require_main = __commonJS({
3159
3159
  // src/utils/crypto-helper.ts
3160
3160
  function loadPasswordFromConfig() {
3161
3161
  try {
3162
- const configPath = import_path.default.join(process.cwd(), "data", "config.json");
3162
+ const envDir = process.env["MCP_DATA_DIR"];
3163
+ const dataDir = envDir || import_path.default.join(import_os.default.homedir(), ".user-web-feedback", "data");
3164
+ const configPath = import_path.default.join(dataDir, "config.json");
3163
3165
  if (import_fs.default.existsSync(configPath)) {
3164
3166
  const configData = import_fs.default.readFileSync(configPath, "utf8");
3165
3167
  const config2 = JSON.parse(configData);
@@ -3251,7 +3253,7 @@ function maskApiKey(apiKey) {
3251
3253
  const masked = "*".repeat(maskedLength);
3252
3254
  return `${prefix}${masked}${suffix}`;
3253
3255
  }
3254
- var import_crypto, import_fs, import_path, ALGORITHM, IV_LENGTH, SALT, encryptionKeyLogged, cachedPassword;
3256
+ var import_crypto, import_fs, import_path, import_os, ALGORITHM, IV_LENGTH, SALT, encryptionKeyLogged, cachedPassword;
3255
3257
  var init_crypto_helper = __esm({
3256
3258
  "src/utils/crypto-helper.ts"() {
3257
3259
  "use strict";
@@ -3259,6 +3261,7 @@ var init_crypto_helper = __esm({
3259
3261
  import_crypto = __toESM(require("crypto"), 1);
3260
3262
  import_fs = __toESM(require("fs"), 1);
3261
3263
  import_path = __toESM(require("path"), 1);
3264
+ import_os = __toESM(require("os"), 1);
3262
3265
  init_logger();
3263
3266
  ALGORITHM = "aes-256-gcm";
3264
3267
  IV_LENGTH = 16;
@@ -3340,6 +3343,11 @@ __export(database_exports, {
3340
3343
  updatePromptConfigs: () => updatePromptConfigs,
3341
3344
  updateUserPreferences: () => updateUserPreferences
3342
3345
  });
3346
+ function getGlobalDataDir() {
3347
+ const envDir = process.env["MCP_DATA_DIR"];
3348
+ if (envDir) return envDir;
3349
+ return import_path2.default.join(import_os2.default.homedir(), ".user-web-feedback", "data");
3350
+ }
3343
3351
  function hashPrompt(prompt) {
3344
3352
  return import_crypto2.default.createHash("sha256").update(prompt.trim()).digest("hex").substring(0, 16);
3345
3353
  }
@@ -5202,7 +5210,7 @@ function cleanupExpiredPendingResponses() {
5202
5210
  `).run(now, deliveredCutoff);
5203
5211
  return result.changes;
5204
5212
  }
5205
- var import_better_sqlite3, import_path2, import_fs2, import_crypto2, DB_DIR, DB_PATH, db, SYSTEM_PROMPT_VERSIONS, CURRENT_PROMPT_VERSION, DEFAULT_PROMPT_CONFIGS;
5213
+ var import_better_sqlite3, import_path2, import_fs2, import_os2, import_crypto2, DB_DIR, DB_PATH, db, SYSTEM_PROMPT_VERSIONS, CURRENT_PROMPT_VERSION, DEFAULT_PROMPT_CONFIGS;
5206
5214
  var init_database = __esm({
5207
5215
  "src/utils/database.ts"() {
5208
5216
  "use strict";
@@ -5210,10 +5218,11 @@ var init_database = __esm({
5210
5218
  import_better_sqlite3 = __toESM(require("better-sqlite3"), 1);
5211
5219
  import_path2 = __toESM(require("path"), 1);
5212
5220
  import_fs2 = __toESM(require("fs"), 1);
5221
+ import_os2 = __toESM(require("os"), 1);
5213
5222
  import_crypto2 = __toESM(require("crypto"), 1);
5214
5223
  init_crypto_helper();
5215
5224
  init_logger();
5216
- DB_DIR = import_path2.default.join(process.cwd(), "data");
5225
+ DB_DIR = getGlobalDataDir();
5217
5226
  DB_PATH = import_path2.default.join(DB_DIR, "feedback.db");
5218
5227
  db = null;
5219
5228
  SYSTEM_PROMPT_VERSIONS = {
@@ -47318,7 +47327,7 @@ var require_application = __commonJS({
47318
47327
  };
47319
47328
  app.del = deprecate.function(app.delete, "app.del: Use app.delete instead");
47320
47329
  app.render = function render(name, options, callback) {
47321
- var cache2 = this.cache;
47330
+ var cache3 = this.cache;
47322
47331
  var done = callback;
47323
47332
  var engines = this.engines;
47324
47333
  var opts = options;
@@ -47337,7 +47346,7 @@ var require_application = __commonJS({
47337
47346
  renderOptions.cache = this.enabled("view cache");
47338
47347
  }
47339
47348
  if (renderOptions.cache) {
47340
- view = cache2[name];
47349
+ view = cache3[name];
47341
47350
  }
47342
47351
  if (!view) {
47343
47352
  var View2 = this.get("view");
@@ -47353,7 +47362,7 @@ var require_application = __commonJS({
47353
47362
  return done(err);
47354
47363
  }
47355
47364
  if (renderOptions.cache) {
47356
- cache2[name] = view;
47365
+ cache3[name] = view;
47357
47366
  }
47358
47367
  }
47359
47368
  tryRender(view, renderOptions, done);
@@ -50845,7 +50854,7 @@ var require_supports_color = __commonJS({
50845
50854
  "node_modules/.pnpm/supports-color@7.2.0/node_modules/supports-color/index.js"(exports2, module2) {
50846
50855
  "use strict";
50847
50856
  init_cjs_shims();
50848
- var os4 = require("os");
50857
+ var os7 = require("os");
50849
50858
  var tty = require("tty");
50850
50859
  var hasFlag = require_has_flag();
50851
50860
  var { env } = process;
@@ -50893,7 +50902,7 @@ var require_supports_color = __commonJS({
50893
50902
  return min;
50894
50903
  }
50895
50904
  if (process.platform === "win32") {
50896
- const osRelease = os4.release().split(".");
50905
+ const osRelease = os7.release().split(".");
50897
50906
  if (Number(osRelease[0]) >= 10 && Number(osRelease[2]) >= 10586) {
50898
50907
  return Number(osRelease[2]) >= 14931 ? 3 : 2;
50899
50908
  }
@@ -83162,14 +83171,14 @@ var require_is_wsl = __commonJS({
83162
83171
  "node_modules/.pnpm/is-wsl@2.2.0/node_modules/is-wsl/index.js"(exports2, module2) {
83163
83172
  "use strict";
83164
83173
  init_cjs_shims();
83165
- var os4 = require("os");
83174
+ var os7 = require("os");
83166
83175
  var fs9 = require("fs");
83167
83176
  var isDocker2 = require_is_docker();
83168
83177
  var isWsl2 = () => {
83169
83178
  if (process.platform !== "linux") {
83170
83179
  return false;
83171
83180
  }
83172
- if (os4.release().toLowerCase().includes("microsoft")) {
83181
+ if (os7.release().toLowerCase().includes("microsoft")) {
83173
83182
  if (isDocker2()) {
83174
83183
  return false;
83175
83184
  }
@@ -84801,8 +84810,8 @@ var require_untildify = __commonJS({
84801
84810
  "node_modules/.pnpm/untildify@4.0.0/node_modules/untildify/index.js"(exports2, module2) {
84802
84811
  "use strict";
84803
84812
  init_cjs_shims();
84804
- var os4 = require("os");
84805
- var homeDirectory = os4.homedir();
84813
+ var os7 = require("os");
84814
+ var homeDirectory = os7.homedir();
84806
84815
  module2.exports = (pathWithTilde) => {
84807
84816
  if (typeof pathWithTilde !== "string") {
84808
84817
  throw new TypeError(`Expected a string, got ${typeof pathWithTilde}`);
@@ -84840,16 +84849,16 @@ async function defaultBrowserId() {
84840
84849
  }
84841
84850
  return bundleId;
84842
84851
  }
84843
- var import_os, import_fs6, import_bplist_parser, import_untildify, macOsVersion, filePath;
84852
+ var import_os4, import_fs6, import_bplist_parser, import_untildify, macOsVersion, filePath;
84844
84853
  var init_default_browser_id = __esm({
84845
84854
  "node_modules/.pnpm/default-browser-id@3.0.0/node_modules/default-browser-id/index.js"() {
84846
84855
  "use strict";
84847
84856
  init_cjs_shims();
84848
- import_os = __toESM(require("os"), 1);
84857
+ import_os4 = __toESM(require("os"), 1);
84849
84858
  import_fs6 = require("fs");
84850
84859
  import_bplist_parser = __toESM(require_bplistParser(), 1);
84851
84860
  import_untildify = __toESM(require_untildify(), 1);
84852
- macOsVersion = Number(import_os.default.release().split(".")[0]);
84861
+ macOsVersion = Number(import_os4.default.release().split(".")[0]);
84853
84862
  filePath = (0, import_untildify.default)(macOsVersion >= 14 ? "~/Library/Preferences/com.apple.LaunchServices/com.apple.launchservices.secure.plist" : "~/Library/Preferences/com.apple.LaunchServices.plist");
84854
84863
  }
84855
84864
  });
@@ -85698,7 +85707,7 @@ var require_kill = __commonJS({
85698
85707
  "node_modules/.pnpm/execa@5.1.1/node_modules/execa/lib/kill.js"(exports2, module2) {
85699
85708
  "use strict";
85700
85709
  init_cjs_shims();
85701
- var os4 = require("os");
85710
+ var os7 = require("os");
85702
85711
  var onExit2 = require_signal_exit();
85703
85712
  var DEFAULT_FORCE_KILL_TIMEOUT2 = 1e3 * 5;
85704
85713
  var spawnedKill2 = (kill, signal = "SIGTERM", options = {}) => {
@@ -85722,7 +85731,7 @@ var require_kill = __commonJS({
85722
85731
  return isSigterm2(signal) && forceKillAfterTimeout !== false && killResult;
85723
85732
  };
85724
85733
  var isSigterm2 = (signal) => {
85725
- return signal === os4.constants.signals.SIGTERM || typeof signal === "string" && signal.toUpperCase() === "SIGTERM";
85734
+ return signal === os7.constants.signals.SIGTERM || typeof signal === "string" && signal.toUpperCase() === "SIGTERM";
85726
85735
  };
85727
85736
  var getForceKillAfterTimeout2 = ({ forceKillAfterTimeout = true }) => {
85728
85737
  if (forceKillAfterTimeout === true) {
@@ -91353,6 +91362,10 @@ var PortManager = class {
91353
91362
  PORT_RANGE_START = 5e3;
91354
91363
  PORT_RANGE_END = 5099;
91355
91364
  MAX_RETRIES = 20;
91365
+ BROWSER_UNSAFE_PORTS = /* @__PURE__ */ new Set([
91366
+ 5060,
91367
+ 5061
91368
+ ]);
91356
91369
  /**
91357
91370
  * 檢查連接埠是否可用(增強版本)
91358
91371
  */
@@ -91406,6 +91419,10 @@ var PortManager = class {
91406
91419
  */
91407
91420
  async resolvePortConflict(port) {
91408
91421
  logger.info(`\u6AA2\u67E5\u9023\u63A5\u57E0 ${port} \u662F\u5426\u53EF\u7528`);
91422
+ if (this.BROWSER_UNSAFE_PORTS.has(port)) {
91423
+ logger.warn(`\u9023\u63A5\u57E0 ${port} \u662F\u700F\u89BD\u5668\u4E0D\u5B89\u5168\u7AEF\u53E3\uFF0C\u81EA\u52D5\u5C0B\u627E\u66FF\u4EE3`);
91424
+ return await this.findAlternativePort(port);
91425
+ }
91409
91426
  if (await this.isPortAvailable(port)) {
91410
91427
  logger.info(`\u9023\u63A5\u57E0 ${port} \u53EF\u7528\uFF0C\u76F4\u63A5\u4F7F\u7528`);
91411
91428
  return port;
@@ -91423,6 +91440,10 @@ var PortManager = class {
91423
91440
  if (port > 65535) {
91424
91441
  break;
91425
91442
  }
91443
+ if (this.BROWSER_UNSAFE_PORTS.has(port)) {
91444
+ logger.debug(`\u9023\u63A5\u57E0 ${port} \u662F\u700F\u89BD\u5668\u4E0D\u5B89\u5168\u7AEF\u53E3\uFF0C\u8DF3\u904E`);
91445
+ continue;
91446
+ }
91426
91447
  logger.debug(`\u5617\u8A66\u9023\u63A5\u57E0 ${port}...`);
91427
91448
  if (await this.isPortAvailable(port)) {
91428
91449
  logger.info(`\u627E\u5230\u53EF\u7528\u9023\u63A5\u57E0: ${port}`);
@@ -91437,16 +91458,21 @@ var PortManager = class {
91437
91458
  */
91438
91459
  async findAvailablePort(preferredPort) {
91439
91460
  if (preferredPort) {
91440
- logger.debug(`\u6AA2\u67E5\u9996\u9078\u9023\u63A5\u57E0: ${preferredPort}`);
91441
- const available = await this.isPortAvailable(preferredPort);
91442
- if (available) {
91443
- logger.info(`\u4F7F\u7528\u9996\u9078\u9023\u63A5\u57E0: ${preferredPort}`);
91444
- return preferredPort;
91461
+ if (this.BROWSER_UNSAFE_PORTS.has(preferredPort)) {
91462
+ logger.warn(`\u9996\u9078\u9023\u63A5\u57E0 ${preferredPort} \u662F\u700F\u89BD\u5668\u4E0D\u5B89\u5168\u7AEF\u53E3\uFF0C\u8DF3\u904E`);
91445
91463
  } else {
91446
- logger.warn(`\u9996\u9078\u9023\u63A5\u57E0 ${preferredPort} \u4E0D\u53EF\u7528\uFF0C\u5C0B\u627E\u5176\u4ED6\u9023\u63A5\u57E0...`);
91464
+ logger.debug(`\u6AA2\u67E5\u9996\u9078\u9023\u63A5\u57E0: ${preferredPort}`);
91465
+ const available = await this.isPortAvailable(preferredPort);
91466
+ if (available) {
91467
+ logger.info(`\u4F7F\u7528\u9996\u9078\u9023\u63A5\u57E0: ${preferredPort}`);
91468
+ return preferredPort;
91469
+ } else {
91470
+ logger.warn(`\u9996\u9078\u9023\u63A5\u57E0 ${preferredPort} \u4E0D\u53EF\u7528\uFF0C\u5C0B\u627E\u5176\u4ED6\u9023\u63A5\u57E0...`);
91471
+ }
91447
91472
  }
91448
91473
  }
91449
91474
  for (let port = this.PORT_RANGE_START; port <= this.PORT_RANGE_END; port++) {
91475
+ if (this.BROWSER_UNSAFE_PORTS.has(port)) continue;
91450
91476
  logger.debug(`\u6AA2\u67E5\u9023\u63A5\u57E0: ${port}`);
91451
91477
  if (await this.isPortAvailable(port)) {
91452
91478
  logger.info(`\u627E\u5230\u53EF\u7528\u9023\u63A5\u57E0: ${port}`);
@@ -91455,6 +91481,7 @@ var PortManager = class {
91455
91481
  }
91456
91482
  for (let i = 0; i < this.MAX_RETRIES; i++) {
91457
91483
  const randomPort = Math.floor(Math.random() * (65535 - 1024) + 1024);
91484
+ if (this.BROWSER_UNSAFE_PORTS.has(randomPort)) continue;
91458
91485
  logger.debug(`\u5617\u8A66\u96A8\u6A5F\u9023\u63A5\u57E0: ${randomPort}`);
91459
91486
  if (await this.isPortAvailable(randomPort)) {
91460
91487
  logger.info(`\u627E\u5230\u96A8\u6A5F\u53EF\u7528\u9023\u63A5\u57E0: ${randomPort}`);
@@ -92598,6 +92625,7 @@ function getPackageVersion() {
92598
92625
  init_cjs_shims();
92599
92626
  var import_fs5 = require("fs");
92600
92627
  var import_path5 = __toESM(require("path"), 1);
92628
+ var import_os3 = __toESM(require("os"), 1);
92601
92629
  init_logger();
92602
92630
  var DEFAULT_LOCK_FILENAME = ".user-feedback.lock";
92603
92631
  var DEFAULT_HEALTH_CHECK_TIMEOUT = 3e3;
@@ -92607,7 +92635,8 @@ var InstanceLock = class {
92607
92635
  if (this.lockFilePath) {
92608
92636
  return this.lockFilePath;
92609
92637
  }
92610
- const dataDir = import_path5.default.resolve(process.cwd(), "data");
92638
+ const envDir = process.env["MCP_DATA_DIR"];
92639
+ const dataDir = envDir || import_path5.default.join(import_os3.default.homedir(), ".user-web-feedback", "data");
92611
92640
  if (!(0, import_fs5.existsSync)(dataDir)) {
92612
92641
  (0, import_fs5.mkdirSync)(dataDir, { recursive: true });
92613
92642
  }
@@ -92877,6 +92906,424 @@ var SelfProbeService = class {
92877
92906
  // src/server/web-server.ts
92878
92907
  init_crypto_helper();
92879
92908
  init_ai_service();
92909
+
92910
+ // src/utils/ai-provider-factory.ts
92911
+ init_cjs_shims();
92912
+ init_database();
92913
+ init_logger();
92914
+
92915
+ // src/utils/api-provider.ts
92916
+ init_cjs_shims();
92917
+ init_dist();
92918
+ init_database();
92919
+ init_logger();
92920
+ init_mcp_client_manager();
92921
+ init_prompt_aggregator2();
92922
+ var MAX_RETRIES2 = 3;
92923
+ var RETRY_DELAYS2 = [1e3, 2e3, 4e3];
92924
+ var cache2 = /* @__PURE__ */ new Map();
92925
+ var CACHE_TTL3 = 5 * 60 * 1e3;
92926
+ function getProviderFromUrl2(apiUrl) {
92927
+ if (!apiUrl) return "google";
92928
+ const normalizedUrl = apiUrl.toLowerCase();
92929
+ if (normalizedUrl.includes("api.openai.com")) return "openai";
92930
+ if (normalizedUrl.includes("api.anthropic.com")) return "anthropic";
92931
+ if (normalizedUrl.includes("generativelanguage.googleapis.com")) return "google";
92932
+ if (normalizedUrl.includes("nvidia.com")) return "nvidia";
92933
+ if (normalizedUrl.includes("bigmodel.cn") || normalizedUrl.includes("z.ai")) return "zai";
92934
+ return "openai";
92935
+ }
92936
+ var APIProvider = class {
92937
+ getName() {
92938
+ return "API Provider";
92939
+ }
92940
+ getMode() {
92941
+ return "api";
92942
+ }
92943
+ async isAvailable() {
92944
+ const settings = getAISettings();
92945
+ return !!(settings?.apiKey && settings.apiKey !== "YOUR_API_KEY_HERE");
92946
+ }
92947
+ async generateReply(request) {
92948
+ try {
92949
+ const cacheKey = `${request.aiMessage}:${request.userContext || ""}`;
92950
+ if (!request.toolResults) {
92951
+ const cached2 = cache2.get(cacheKey);
92952
+ if (cached2 && Date.now() - cached2.timestamp < CACHE_TTL3) {
92953
+ logger.debug("[APIProvider] \u4F7F\u7528\u5FEB\u53D6\u56DE\u8986");
92954
+ return { success: true, reply: cached2.reply, mode: "api" };
92955
+ }
92956
+ }
92957
+ const settings = getAISettings();
92958
+ if (!settings || !settings.apiKey || settings.apiKey === "YOUR_API_KEY_HERE") {
92959
+ logger.warn("[APIProvider] API Key \u672A\u8A2D\u5B9A\u6216\u7121\u6548");
92960
+ return { success: false, error: "\u8ACB\u5148\u5728\u8A2D\u5B9A\u4E2D\u914D\u7F6E AI API Key", mode: "api" };
92961
+ }
92962
+ const aggregator = getPromptAggregator();
92963
+ const cliSettings = getCLISettings();
92964
+ let mcpTools = [];
92965
+ if (request.includeMCPTools) {
92966
+ try {
92967
+ const allTools = mcpClientManager.getAllTools();
92968
+ mcpTools = allTools.map((tool) => ({
92969
+ name: tool.name,
92970
+ description: tool.description,
92971
+ inputSchema: tool.inputSchema
92972
+ }));
92973
+ } catch (error2) {
92974
+ logger.warn("[APIProvider] \u7121\u6CD5\u53D6\u5F97 MCP \u5DE5\u5177", error2);
92975
+ }
92976
+ }
92977
+ const context = aggregator.buildContextSync(request, settings, cliSettings, mcpTools);
92978
+ context.mode = "api";
92979
+ const aggregated = aggregator.aggregate(context);
92980
+ const promptSent = aggregated.fullPrompt;
92981
+ const provider = getProviderFromUrl2(settings.apiUrl);
92982
+ logger.info(`[APIProvider] \u4F7F\u7528 ${provider} \u63D0\u4F9B\u5546, apiUrl: ${settings.apiUrl || "(\u9810\u8A2D)"}`);
92983
+ let reply;
92984
+ if (provider !== "google") {
92985
+ reply = await this.generateWithOpenAI(
92986
+ settings.apiKey,
92987
+ settings.model,
92988
+ settings.apiUrl,
92989
+ promptSent,
92990
+ settings.temperature,
92991
+ settings.maxTokens
92992
+ );
92993
+ } else {
92994
+ reply = await this.generateWithGoogle(
92995
+ settings.apiKey,
92996
+ settings.model,
92997
+ promptSent,
92998
+ settings.temperature,
92999
+ settings.maxTokens
93000
+ );
93001
+ }
93002
+ if (!request.toolResults) {
93003
+ cache2.set(cacheKey, { reply, timestamp: Date.now() });
93004
+ this.cleanExpiredCache();
93005
+ }
93006
+ return { success: true, reply, promptSent, mode: "api" };
93007
+ } catch (error2) {
93008
+ logger.error("[APIProvider] \u751F\u6210\u56DE\u8986\u5931\u6557", error2);
93009
+ return {
93010
+ success: false,
93011
+ error: error2 instanceof Error ? error2.message : "\u672A\u77E5\u932F\u8AA4",
93012
+ mode: "api"
93013
+ };
93014
+ }
93015
+ }
93016
+ async generateWithGoogle(apiKey, model, prompt, temperature, maxTokens, retryCount = 0) {
93017
+ try {
93018
+ const genAI = new GoogleGenerativeAI(apiKey);
93019
+ const generativeModel = genAI.getGenerativeModel({
93020
+ model,
93021
+ generationConfig: {
93022
+ temperature: temperature ?? 0.7,
93023
+ maxOutputTokens: maxTokens ?? 1e3
93024
+ }
93025
+ });
93026
+ const result = await generativeModel.generateContent(prompt);
93027
+ const response = await result.response;
93028
+ const text = response.text();
93029
+ if (!text) throw new Error("AI \u56DE\u8986\u70BA\u7A7A");
93030
+ return text;
93031
+ } catch (error2) {
93032
+ if (error2 instanceof Error) {
93033
+ if (error2.message.includes("429") || error2.message.includes("quota")) {
93034
+ if (retryCount < MAX_RETRIES2) {
93035
+ await this.sleep(RETRY_DELAYS2[retryCount] || 4e3);
93036
+ return this.generateWithGoogle(apiKey, model, prompt, temperature, maxTokens, retryCount + 1);
93037
+ }
93038
+ throw new Error("API \u914D\u984D\u5DF2\u7528\u76E1\u6216\u901F\u7387\u9650\u5236\uFF0C\u8ACB\u7A0D\u5F8C\u518D\u8A66");
93039
+ }
93040
+ if (error2.message.includes("API key") || error2.message.includes("401")) {
93041
+ throw new Error("API Key \u7121\u6548\uFF0C\u8ACB\u6AA2\u67E5\u8A2D\u5B9A");
93042
+ }
93043
+ if (retryCount < MAX_RETRIES2) {
93044
+ await this.sleep(RETRY_DELAYS2[retryCount] || 4e3);
93045
+ return this.generateWithGoogle(apiKey, model, prompt, temperature, maxTokens, retryCount + 1);
93046
+ }
93047
+ }
93048
+ throw error2;
93049
+ }
93050
+ }
93051
+ async generateWithOpenAI(apiKey, model, apiUrl, prompt, temperature, maxTokens, retryCount = 0) {
93052
+ try {
93053
+ const OpenAI = (await import("openai")).default;
93054
+ const client = new OpenAI({
93055
+ apiKey,
93056
+ baseURL: apiUrl || "https://api.openai.com/v1"
93057
+ });
93058
+ const response = await client.chat.completions.create({
93059
+ model,
93060
+ messages: [{ role: "user", content: prompt }],
93061
+ temperature: temperature ?? 0.7,
93062
+ max_tokens: maxTokens ?? 1e3
93063
+ });
93064
+ const text = response.choices?.[0]?.message?.content;
93065
+ if (!text) throw new Error("AI \u56DE\u8986\u70BA\u7A7A");
93066
+ return text;
93067
+ } catch (error2) {
93068
+ if (error2 instanceof Error) {
93069
+ if (error2.message.includes("429") || error2.message.includes("quota") || error2.message.includes("rate")) {
93070
+ if (retryCount < MAX_RETRIES2) {
93071
+ await this.sleep(RETRY_DELAYS2[retryCount] || 4e3);
93072
+ return this.generateWithOpenAI(apiKey, model, apiUrl, prompt, temperature, maxTokens, retryCount + 1);
93073
+ }
93074
+ throw new Error("API \u914D\u984D\u5DF2\u7528\u76E1\u6216\u901F\u7387\u9650\u5236\uFF0C\u8ACB\u7A0D\u5F8C\u518D\u8A66");
93075
+ }
93076
+ if (error2.message.includes("API key") || error2.message.includes("401") || error2.message.includes("Unauthorized")) {
93077
+ throw new Error("API Key \u7121\u6548\uFF0C\u8ACB\u6AA2\u67E5\u8A2D\u5B9A");
93078
+ }
93079
+ if (retryCount < MAX_RETRIES2) {
93080
+ await this.sleep(RETRY_DELAYS2[retryCount] || 4e3);
93081
+ return this.generateWithOpenAI(apiKey, model, apiUrl, prompt, temperature, maxTokens, retryCount + 1);
93082
+ }
93083
+ }
93084
+ throw error2;
93085
+ }
93086
+ }
93087
+ sleep(ms) {
93088
+ return new Promise((resolve2) => setTimeout(resolve2, ms));
93089
+ }
93090
+ cleanExpiredCache() {
93091
+ const now = Date.now();
93092
+ for (const [key, entry] of cache2.entries()) {
93093
+ if (now - entry.timestamp > CACHE_TTL3) cache2.delete(key);
93094
+ }
93095
+ }
93096
+ };
93097
+
93098
+ // src/utils/cli-provider.ts
93099
+ init_cjs_shims();
93100
+ init_database();
93101
+ init_logger();
93102
+ init_cli_executor();
93103
+ init_cli_detector();
93104
+ init_mcp_client_manager();
93105
+ init_prompt_aggregator2();
93106
+ var CLIProvider = class {
93107
+ cliSettings;
93108
+ constructor(cliSettings) {
93109
+ this.cliSettings = cliSettings;
93110
+ }
93111
+ getName() {
93112
+ return `CLI (${this.cliSettings.cliTool})`;
93113
+ }
93114
+ getMode() {
93115
+ return "cli";
93116
+ }
93117
+ async isAvailable() {
93118
+ return isToolAvailable(this.cliSettings.cliTool);
93119
+ }
93120
+ async generateReply(request) {
93121
+ const tool = this.cliSettings.cliTool;
93122
+ const available = await this.isAvailable();
93123
+ if (!available) {
93124
+ logger.warn(`[CLIProvider] CLI tool not available: ${tool}`);
93125
+ return {
93126
+ success: false,
93127
+ error: `CLI \u5DE5\u5177 ${tool} \u672A\u5B89\u88DD\u6216\u4E0D\u53EF\u7528`,
93128
+ mode: "cli",
93129
+ cliTool: tool
93130
+ };
93131
+ }
93132
+ const terminalId = `${request.projectPath || "default"}-${tool}`.replace(/[^a-zA-Z0-9-]/g, "_");
93133
+ let terminal = getCLITerminalById(terminalId);
93134
+ if (!terminal) {
93135
+ terminal = createCLITerminal({
93136
+ id: terminalId,
93137
+ projectName: request.projectName || "\u672A\u547D\u540D\u5C08\u6848",
93138
+ projectPath: request.projectPath || "",
93139
+ tool,
93140
+ status: "running",
93141
+ pid: void 0
93142
+ });
93143
+ } else {
93144
+ updateCLITerminal(terminalId, { status: "running" });
93145
+ }
93146
+ const aggregator = getPromptAggregator();
93147
+ const settings = getAISettings();
93148
+ const cliSettings = getCLISettings();
93149
+ let mcpTools = [];
93150
+ if (request.includeMCPTools) {
93151
+ try {
93152
+ const allTools = mcpClientManager.getAllTools();
93153
+ mcpTools = allTools.map((t) => ({
93154
+ name: t.name,
93155
+ description: t.description,
93156
+ inputSchema: t.inputSchema
93157
+ }));
93158
+ } catch {
93159
+ logger.warn("[CLIProvider] \u7121\u6CD5\u7372\u53D6 MCP \u5DE5\u5177");
93160
+ }
93161
+ }
93162
+ const context = aggregator.buildContextSync(request, settings ?? null, cliSettings, mcpTools);
93163
+ context.mode = "cli";
93164
+ const aggregated = aggregator.aggregate(context);
93165
+ const prompt = aggregated.fullPrompt;
93166
+ logger.info(`[CLIProvider] Prompt \u5927\u5C0F\u5206\u6790:`, {
93167
+ totalLength: prompt.length,
93168
+ totalKB: (prompt.length / 1024).toFixed(2) + " KB",
93169
+ mcpToolsCount: mcpTools.length,
93170
+ mcpToolsSize: JSON.stringify(mcpTools).length,
93171
+ systemPromptLength: settings?.systemPrompt?.length || 0,
93172
+ aiMessageLength: request.aiMessage?.length || 0,
93173
+ userContextLength: request.userContext?.length || 0,
93174
+ timeout: this.cliSettings.cliTimeout
93175
+ });
93176
+ try {
93177
+ const result = await executeCLI({
93178
+ tool,
93179
+ prompt,
93180
+ timeout: this.cliSettings.cliTimeout,
93181
+ workingDirectory: request.projectPath,
93182
+ outputFormat: "text"
93183
+ });
93184
+ insertCLIExecutionLog({
93185
+ terminalId,
93186
+ prompt: prompt.substring(0, 1e3),
93187
+ response: result.success ? result.output.substring(0, 5e3) : null,
93188
+ executionTime: result.executionTime,
93189
+ success: result.success,
93190
+ error: result.error
93191
+ });
93192
+ if (!result.success) {
93193
+ updateCLITerminal(terminalId, { status: "error" });
93194
+ return {
93195
+ success: false,
93196
+ error: result.error || "CLI \u57F7\u884C\u5931\u6557",
93197
+ mode: "cli",
93198
+ cliTool: tool,
93199
+ promptSent: prompt
93200
+ };
93201
+ }
93202
+ if (request.includeMCPTools) {
93203
+ const mcpHandler = createCLIMCPHandler(settings?.maxToolRounds || 10);
93204
+ updateCLITerminal(terminalId, { status: "mcp-processing" });
93205
+ const handlerResult = await mcpHandler.handleResponse(result.output, (call) => {
93206
+ logger.info(`[CLIProvider] \u57F7\u884C MCP \u5DE5\u5177: ${call.toolName}`);
93207
+ });
93208
+ if (handlerResult.toolCallsDetected) {
93209
+ insertCLIExecutionLog({
93210
+ terminalId,
93211
+ prompt: `[MCP] ${handlerResult.toolResults.length} \u500B\u5DE5\u5177\u547C\u53EB`,
93212
+ response: handlerResult.finalResponse.substring(0, 5e3),
93213
+ executionTime: handlerResult.toolResults.reduce((sum, r) => sum + r.executionTime, 0),
93214
+ success: true,
93215
+ error: void 0
93216
+ });
93217
+ }
93218
+ updateCLITerminal(terminalId, { status: "idle" });
93219
+ return {
93220
+ success: true,
93221
+ reply: handlerResult.finalResponse,
93222
+ mode: "cli",
93223
+ cliTool: tool,
93224
+ promptSent: prompt
93225
+ };
93226
+ }
93227
+ updateCLITerminal(terminalId, { status: "idle" });
93228
+ return {
93229
+ success: true,
93230
+ reply: result.output,
93231
+ mode: "cli",
93232
+ cliTool: tool,
93233
+ promptSent: prompt
93234
+ };
93235
+ } catch (error2) {
93236
+ insertCLIExecutionLog({
93237
+ terminalId,
93238
+ prompt: prompt.substring(0, 1e3),
93239
+ response: null,
93240
+ executionTime: 0,
93241
+ success: false,
93242
+ error: error2 instanceof Error ? error2.message : "\u672A\u77E5\u932F\u8AA4"
93243
+ });
93244
+ updateCLITerminal(terminalId, { status: "error" });
93245
+ return {
93246
+ success: false,
93247
+ error: error2 instanceof Error ? error2.message : "\u672A\u77E5\u932F\u8AA4",
93248
+ mode: "cli",
93249
+ cliTool: tool,
93250
+ promptSent: prompt
93251
+ };
93252
+ }
93253
+ }
93254
+ };
93255
+
93256
+ // src/utils/ai-provider-factory.ts
93257
+ var AIProviderFactory = class _AIProviderFactory {
93258
+ static instance;
93259
+ currentProvider = null;
93260
+ fallbackProvider = null;
93261
+ constructor() {
93262
+ }
93263
+ /**
93264
+ * 取得工廠實例 (Singleton)
93265
+ */
93266
+ static getInstance() {
93267
+ if (!_AIProviderFactory.instance) {
93268
+ _AIProviderFactory.instance = new _AIProviderFactory();
93269
+ }
93270
+ return _AIProviderFactory.instance;
93271
+ }
93272
+ /**
93273
+ * 根據設定取得適當的 Provider
93274
+ */
93275
+ async getProvider() {
93276
+ const cliSettings = getCLISettings();
93277
+ const mode = cliSettings?.aiMode === "cli" ? "cli" : "api";
93278
+ logger.debug("[AIProviderFactory] \u53D6\u5F97 Provider", { mode, cliTool: cliSettings?.cliTool });
93279
+ if (mode === "cli") {
93280
+ const cliProvider = new CLIProvider(cliSettings);
93281
+ const available = await cliProvider.isAvailable();
93282
+ if (!available) {
93283
+ logger.warn("[AIProviderFactory] CLI Provider \u4E0D\u53EF\u7528");
93284
+ if (cliSettings?.cliFallbackToApi) {
93285
+ logger.info("[AIProviderFactory] \u56DE\u9000\u5230 API Provider");
93286
+ return new APIProvider();
93287
+ }
93288
+ throw new Error(`CLI \u5DE5\u5177 ${cliSettings?.cliTool} \u4E0D\u53EF\u7528\uFF0C\u4E14\u672A\u555F\u7528 API \u56DE\u9000`);
93289
+ }
93290
+ return cliProvider;
93291
+ }
93292
+ return new APIProvider();
93293
+ }
93294
+ /**
93295
+ * 取得當前模式
93296
+ */
93297
+ getCurrentMode() {
93298
+ const cliSettings = getCLISettings();
93299
+ return cliSettings?.aiMode === "cli" ? "cli" : "api";
93300
+ }
93301
+ /**
93302
+ * 取得當前 CLI 工具名稱
93303
+ */
93304
+ getCurrentCLITool() {
93305
+ const cliSettings = getCLISettings();
93306
+ return cliSettings?.cliTool;
93307
+ }
93308
+ /**
93309
+ * 使用工廠生成 AI 回覆
93310
+ */
93311
+ async generateReply(request) {
93312
+ try {
93313
+ const provider = await this.getProvider();
93314
+ logger.info(`[AIProviderFactory] \u4F7F\u7528 ${provider.getName()} \u751F\u6210\u56DE\u8986`);
93315
+ return provider.generateReply(request);
93316
+ } catch (error2) {
93317
+ logger.error("[AIProviderFactory] \u751F\u6210\u56DE\u8986\u5931\u6557", error2);
93318
+ return {
93319
+ success: false,
93320
+ error: error2 instanceof Error ? error2.message : "\u672A\u77E5\u932F\u8AA4"
93321
+ };
93322
+ }
93323
+ }
93324
+ };
93325
+
93326
+ // src/server/web-server.ts
92880
93327
  init_mcp_client_manager();
92881
93328
  init_cli_detector();
92882
93329
 
@@ -93915,8 +94362,9 @@ var WebServer = class {
93915
94362
  });
93916
94363
  return;
93917
94364
  }
93918
- logger.info("\u958B\u59CB\u751F\u6210 AI \u56DE\u8986");
93919
- const result = await generateAIReply(data);
94365
+ logger.info("\u958B\u59CB\u751F\u6210 AI \u56DE\u8986 (AIProviderFactory)");
94366
+ const factory = AIProviderFactory.getInstance();
94367
+ const result = await factory.generateReply(data);
93920
94368
  if (result.success) {
93921
94369
  logger.info("AI \u56DE\u8986\u751F\u6210\u6210\u529F");
93922
94370
  } else {
@@ -95294,7 +95742,8 @@ var WebServer = class {
95294
95742
  const autoReplyTimer = setTimeout(async () => {
95295
95743
  logger.info(`\u89F8\u767C\u81EA\u52D5\u56DE\u8986: \u6703\u8A71 ${sessionId}`);
95296
95744
  try {
95297
- const result = await generateAIReply({
95745
+ const factory = AIProviderFactory.getInstance();
95746
+ const result = await factory.generateReply({
95298
95747
  aiMessage: workSummary,
95299
95748
  userContext: "\u4F7F\u7528\u8005\u672A\u5728\u6642\u9593\u5167\u56DE\u61C9\uFF0C\u7CFB\u7D71\u81EA\u52D5\u751F\u6210\u56DE\u8986"
95300
95749
  });
@@ -96408,7 +96857,7 @@ var import_url3 = require("url");
96408
96857
  init_cjs_shims();
96409
96858
  var import_events4 = require("events");
96410
96859
  var import_child_process4 = require("child_process");
96411
- var os3 = __toESM(require("os"), 1);
96860
+ var os6 = __toESM(require("os"), 1);
96412
96861
 
96413
96862
  // src/supervisor/ipc-bridge.ts
96414
96863
  init_cjs_shims();
@@ -96904,8 +97353,8 @@ var SupervisorService = class extends import_events4.EventEmitter {
96904
97353
  system: {
96905
97354
  platform: process.platform,
96906
97355
  nodeVersion: process.version,
96907
- totalMemory: os3.totalmem(),
96908
- freeMemory: os3.freemem()
97356
+ totalMemory: os6.totalmem(),
97357
+ freeMemory: os6.freemem()
96909
97358
  },
96910
97359
  restartHistory: this.restartHistory
96911
97360
  };