@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.
- package/dist/cli.cjs +482 -33
- package/dist/index.cjs +479 -30
- package/package.json +1 -1
package/dist/index.cjs
CHANGED
|
@@ -20517,7 +20517,9 @@ var init_stdio = __esm({
|
|
|
20517
20517
|
// src/utils/crypto-helper.ts
|
|
20518
20518
|
function loadPasswordFromConfig() {
|
|
20519
20519
|
try {
|
|
20520
|
-
const
|
|
20520
|
+
const envDir = process.env["MCP_DATA_DIR"];
|
|
20521
|
+
const dataDir = envDir || import_path.default.join(import_os.default.homedir(), ".user-web-feedback", "data");
|
|
20522
|
+
const configPath = import_path.default.join(dataDir, "config.json");
|
|
20521
20523
|
if (import_fs.default.existsSync(configPath)) {
|
|
20522
20524
|
const configData = import_fs.default.readFileSync(configPath, "utf8");
|
|
20523
20525
|
const config2 = JSON.parse(configData);
|
|
@@ -20609,7 +20611,7 @@ function maskApiKey(apiKey) {
|
|
|
20609
20611
|
const masked = "*".repeat(maskedLength);
|
|
20610
20612
|
return `${prefix}${masked}${suffix}`;
|
|
20611
20613
|
}
|
|
20612
|
-
var import_crypto, import_fs, import_path, ALGORITHM, IV_LENGTH, SALT, encryptionKeyLogged, cachedPassword;
|
|
20614
|
+
var import_crypto, import_fs, import_path, import_os, ALGORITHM, IV_LENGTH, SALT, encryptionKeyLogged, cachedPassword;
|
|
20613
20615
|
var init_crypto_helper = __esm({
|
|
20614
20616
|
"src/utils/crypto-helper.ts"() {
|
|
20615
20617
|
"use strict";
|
|
@@ -20617,6 +20619,7 @@ var init_crypto_helper = __esm({
|
|
|
20617
20619
|
import_crypto = __toESM(require("crypto"), 1);
|
|
20618
20620
|
import_fs = __toESM(require("fs"), 1);
|
|
20619
20621
|
import_path = __toESM(require("path"), 1);
|
|
20622
|
+
import_os = __toESM(require("os"), 1);
|
|
20620
20623
|
init_logger();
|
|
20621
20624
|
ALGORITHM = "aes-256-gcm";
|
|
20622
20625
|
IV_LENGTH = 16;
|
|
@@ -20698,6 +20701,11 @@ __export(database_exports, {
|
|
|
20698
20701
|
updatePromptConfigs: () => updatePromptConfigs,
|
|
20699
20702
|
updateUserPreferences: () => updateUserPreferences
|
|
20700
20703
|
});
|
|
20704
|
+
function getGlobalDataDir() {
|
|
20705
|
+
const envDir = process.env["MCP_DATA_DIR"];
|
|
20706
|
+
if (envDir) return envDir;
|
|
20707
|
+
return import_path2.default.join(import_os2.default.homedir(), ".user-web-feedback", "data");
|
|
20708
|
+
}
|
|
20701
20709
|
function hashPrompt(prompt) {
|
|
20702
20710
|
return import_crypto2.default.createHash("sha256").update(prompt.trim()).digest("hex").substring(0, 16);
|
|
20703
20711
|
}
|
|
@@ -22560,7 +22568,7 @@ function cleanupExpiredPendingResponses() {
|
|
|
22560
22568
|
`).run(now, deliveredCutoff);
|
|
22561
22569
|
return result.changes;
|
|
22562
22570
|
}
|
|
22563
|
-
var import_better_sqlite3, import_path2, import_fs2, import_crypto2, DB_DIR, DB_PATH, db, SYSTEM_PROMPT_VERSIONS, CURRENT_PROMPT_VERSION, DEFAULT_PROMPT_CONFIGS;
|
|
22571
|
+
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;
|
|
22564
22572
|
var init_database = __esm({
|
|
22565
22573
|
"src/utils/database.ts"() {
|
|
22566
22574
|
"use strict";
|
|
@@ -22568,10 +22576,11 @@ var init_database = __esm({
|
|
|
22568
22576
|
import_better_sqlite3 = __toESM(require("better-sqlite3"), 1);
|
|
22569
22577
|
import_path2 = __toESM(require("path"), 1);
|
|
22570
22578
|
import_fs2 = __toESM(require("fs"), 1);
|
|
22579
|
+
import_os2 = __toESM(require("os"), 1);
|
|
22571
22580
|
import_crypto2 = __toESM(require("crypto"), 1);
|
|
22572
22581
|
init_crypto_helper();
|
|
22573
22582
|
init_logger();
|
|
22574
|
-
DB_DIR =
|
|
22583
|
+
DB_DIR = getGlobalDataDir();
|
|
22575
22584
|
DB_PATH = import_path2.default.join(DB_DIR, "feedback.db");
|
|
22576
22585
|
db = null;
|
|
22577
22586
|
SYSTEM_PROMPT_VERSIONS = {
|
|
@@ -44205,7 +44214,7 @@ var require_application = __commonJS({
|
|
|
44205
44214
|
};
|
|
44206
44215
|
app.del = deprecate.function(app.delete, "app.del: Use app.delete instead");
|
|
44207
44216
|
app.render = function render(name, options, callback) {
|
|
44208
|
-
var
|
|
44217
|
+
var cache3 = this.cache;
|
|
44209
44218
|
var done = callback;
|
|
44210
44219
|
var engines = this.engines;
|
|
44211
44220
|
var opts = options;
|
|
@@ -44224,7 +44233,7 @@ var require_application = __commonJS({
|
|
|
44224
44233
|
renderOptions.cache = this.enabled("view cache");
|
|
44225
44234
|
}
|
|
44226
44235
|
if (renderOptions.cache) {
|
|
44227
|
-
view =
|
|
44236
|
+
view = cache3[name];
|
|
44228
44237
|
}
|
|
44229
44238
|
if (!view) {
|
|
44230
44239
|
var View2 = this.get("view");
|
|
@@ -44240,7 +44249,7 @@ var require_application = __commonJS({
|
|
|
44240
44249
|
return done(err);
|
|
44241
44250
|
}
|
|
44242
44251
|
if (renderOptions.cache) {
|
|
44243
|
-
|
|
44252
|
+
cache3[name] = view;
|
|
44244
44253
|
}
|
|
44245
44254
|
}
|
|
44246
44255
|
tryRender(view, renderOptions, done);
|
|
@@ -47732,7 +47741,7 @@ var require_supports_color = __commonJS({
|
|
|
47732
47741
|
"node_modules/.pnpm/supports-color@7.2.0/node_modules/supports-color/index.js"(exports2, module2) {
|
|
47733
47742
|
"use strict";
|
|
47734
47743
|
init_cjs_shims();
|
|
47735
|
-
var
|
|
47744
|
+
var os6 = require("os");
|
|
47736
47745
|
var tty = require("tty");
|
|
47737
47746
|
var hasFlag = require_has_flag();
|
|
47738
47747
|
var { env } = process;
|
|
@@ -47780,7 +47789,7 @@ var require_supports_color = __commonJS({
|
|
|
47780
47789
|
return min;
|
|
47781
47790
|
}
|
|
47782
47791
|
if (process.platform === "win32") {
|
|
47783
|
-
const osRelease =
|
|
47792
|
+
const osRelease = os6.release().split(".");
|
|
47784
47793
|
if (Number(osRelease[0]) >= 10 && Number(osRelease[2]) >= 10586) {
|
|
47785
47794
|
return Number(osRelease[2]) >= 14931 ? 3 : 2;
|
|
47786
47795
|
}
|
|
@@ -80049,14 +80058,14 @@ var require_is_wsl = __commonJS({
|
|
|
80049
80058
|
"node_modules/.pnpm/is-wsl@2.2.0/node_modules/is-wsl/index.js"(exports2, module2) {
|
|
80050
80059
|
"use strict";
|
|
80051
80060
|
init_cjs_shims();
|
|
80052
|
-
var
|
|
80061
|
+
var os6 = require("os");
|
|
80053
80062
|
var fs9 = require("fs");
|
|
80054
80063
|
var isDocker2 = require_is_docker();
|
|
80055
80064
|
var isWsl2 = () => {
|
|
80056
80065
|
if (process.platform !== "linux") {
|
|
80057
80066
|
return false;
|
|
80058
80067
|
}
|
|
80059
|
-
if (
|
|
80068
|
+
if (os6.release().toLowerCase().includes("microsoft")) {
|
|
80060
80069
|
if (isDocker2()) {
|
|
80061
80070
|
return false;
|
|
80062
80071
|
}
|
|
@@ -81688,8 +81697,8 @@ var require_untildify = __commonJS({
|
|
|
81688
81697
|
"node_modules/.pnpm/untildify@4.0.0/node_modules/untildify/index.js"(exports2, module2) {
|
|
81689
81698
|
"use strict";
|
|
81690
81699
|
init_cjs_shims();
|
|
81691
|
-
var
|
|
81692
|
-
var homeDirectory =
|
|
81700
|
+
var os6 = require("os");
|
|
81701
|
+
var homeDirectory = os6.homedir();
|
|
81693
81702
|
module2.exports = (pathWithTilde) => {
|
|
81694
81703
|
if (typeof pathWithTilde !== "string") {
|
|
81695
81704
|
throw new TypeError(`Expected a string, got ${typeof pathWithTilde}`);
|
|
@@ -81727,16 +81736,16 @@ async function defaultBrowserId() {
|
|
|
81727
81736
|
}
|
|
81728
81737
|
return bundleId;
|
|
81729
81738
|
}
|
|
81730
|
-
var
|
|
81739
|
+
var import_os4, import_fs6, import_bplist_parser, import_untildify, macOsVersion, filePath;
|
|
81731
81740
|
var init_default_browser_id = __esm({
|
|
81732
81741
|
"node_modules/.pnpm/default-browser-id@3.0.0/node_modules/default-browser-id/index.js"() {
|
|
81733
81742
|
"use strict";
|
|
81734
81743
|
init_cjs_shims();
|
|
81735
|
-
|
|
81744
|
+
import_os4 = __toESM(require("os"), 1);
|
|
81736
81745
|
import_fs6 = require("fs");
|
|
81737
81746
|
import_bplist_parser = __toESM(require_bplistParser(), 1);
|
|
81738
81747
|
import_untildify = __toESM(require_untildify(), 1);
|
|
81739
|
-
macOsVersion = Number(
|
|
81748
|
+
macOsVersion = Number(import_os4.default.release().split(".")[0]);
|
|
81740
81749
|
filePath = (0, import_untildify.default)(macOsVersion >= 14 ? "~/Library/Preferences/com.apple.LaunchServices/com.apple.launchservices.secure.plist" : "~/Library/Preferences/com.apple.LaunchServices.plist");
|
|
81741
81750
|
}
|
|
81742
81751
|
});
|
|
@@ -82585,7 +82594,7 @@ var require_kill = __commonJS({
|
|
|
82585
82594
|
"node_modules/.pnpm/execa@5.1.1/node_modules/execa/lib/kill.js"(exports2, module2) {
|
|
82586
82595
|
"use strict";
|
|
82587
82596
|
init_cjs_shims();
|
|
82588
|
-
var
|
|
82597
|
+
var os6 = require("os");
|
|
82589
82598
|
var onExit2 = require_signal_exit();
|
|
82590
82599
|
var DEFAULT_FORCE_KILL_TIMEOUT2 = 1e3 * 5;
|
|
82591
82600
|
var spawnedKill2 = (kill, signal = "SIGTERM", options = {}) => {
|
|
@@ -82609,7 +82618,7 @@ var require_kill = __commonJS({
|
|
|
82609
82618
|
return isSigterm2(signal) && forceKillAfterTimeout !== false && killResult;
|
|
82610
82619
|
};
|
|
82611
82620
|
var isSigterm2 = (signal) => {
|
|
82612
|
-
return signal ===
|
|
82621
|
+
return signal === os6.constants.signals.SIGTERM || typeof signal === "string" && signal.toUpperCase() === "SIGTERM";
|
|
82613
82622
|
};
|
|
82614
82623
|
var getForceKillAfterTimeout2 = ({ forceKillAfterTimeout = true }) => {
|
|
82615
82624
|
if (forceKillAfterTimeout === true) {
|
|
@@ -85654,7 +85663,7 @@ var require_main2 = __commonJS({
|
|
|
85654
85663
|
init_cjs_shims();
|
|
85655
85664
|
var fs9 = require("fs");
|
|
85656
85665
|
var path9 = require("path");
|
|
85657
|
-
var
|
|
85666
|
+
var os6 = require("os");
|
|
85658
85667
|
var crypto5 = require("crypto");
|
|
85659
85668
|
var packageJson = require_package2();
|
|
85660
85669
|
var version2 = packageJson.version;
|
|
@@ -85777,7 +85786,7 @@ var require_main2 = __commonJS({
|
|
|
85777
85786
|
return null;
|
|
85778
85787
|
}
|
|
85779
85788
|
function _resolveHome(envPath) {
|
|
85780
|
-
return envPath[0] === "~" ? path9.join(
|
|
85789
|
+
return envPath[0] === "~" ? path9.join(os6.homedir(), envPath.slice(1)) : envPath;
|
|
85781
85790
|
}
|
|
85782
85791
|
function _configVault(options) {
|
|
85783
85792
|
const debug = Boolean(options && options.debug);
|
|
@@ -88365,6 +88374,10 @@ var PortManager = class {
|
|
|
88365
88374
|
PORT_RANGE_START = 5e3;
|
|
88366
88375
|
PORT_RANGE_END = 5099;
|
|
88367
88376
|
MAX_RETRIES = 20;
|
|
88377
|
+
BROWSER_UNSAFE_PORTS = /* @__PURE__ */ new Set([
|
|
88378
|
+
5060,
|
|
88379
|
+
5061
|
|
88380
|
+
]);
|
|
88368
88381
|
/**
|
|
88369
88382
|
* 檢查連接埠是否可用(增強版本)
|
|
88370
88383
|
*/
|
|
@@ -88418,6 +88431,10 @@ var PortManager = class {
|
|
|
88418
88431
|
*/
|
|
88419
88432
|
async resolvePortConflict(port) {
|
|
88420
88433
|
logger.info(`\u6AA2\u67E5\u9023\u63A5\u57E0 ${port} \u662F\u5426\u53EF\u7528`);
|
|
88434
|
+
if (this.BROWSER_UNSAFE_PORTS.has(port)) {
|
|
88435
|
+
logger.warn(`\u9023\u63A5\u57E0 ${port} \u662F\u700F\u89BD\u5668\u4E0D\u5B89\u5168\u7AEF\u53E3\uFF0C\u81EA\u52D5\u5C0B\u627E\u66FF\u4EE3`);
|
|
88436
|
+
return await this.findAlternativePort(port);
|
|
88437
|
+
}
|
|
88421
88438
|
if (await this.isPortAvailable(port)) {
|
|
88422
88439
|
logger.info(`\u9023\u63A5\u57E0 ${port} \u53EF\u7528\uFF0C\u76F4\u63A5\u4F7F\u7528`);
|
|
88423
88440
|
return port;
|
|
@@ -88435,6 +88452,10 @@ var PortManager = class {
|
|
|
88435
88452
|
if (port > 65535) {
|
|
88436
88453
|
break;
|
|
88437
88454
|
}
|
|
88455
|
+
if (this.BROWSER_UNSAFE_PORTS.has(port)) {
|
|
88456
|
+
logger.debug(`\u9023\u63A5\u57E0 ${port} \u662F\u700F\u89BD\u5668\u4E0D\u5B89\u5168\u7AEF\u53E3\uFF0C\u8DF3\u904E`);
|
|
88457
|
+
continue;
|
|
88458
|
+
}
|
|
88438
88459
|
logger.debug(`\u5617\u8A66\u9023\u63A5\u57E0 ${port}...`);
|
|
88439
88460
|
if (await this.isPortAvailable(port)) {
|
|
88440
88461
|
logger.info(`\u627E\u5230\u53EF\u7528\u9023\u63A5\u57E0: ${port}`);
|
|
@@ -88449,16 +88470,21 @@ var PortManager = class {
|
|
|
88449
88470
|
*/
|
|
88450
88471
|
async findAvailablePort(preferredPort) {
|
|
88451
88472
|
if (preferredPort) {
|
|
88452
|
-
|
|
88453
|
-
|
|
88454
|
-
if (available) {
|
|
88455
|
-
logger.info(`\u4F7F\u7528\u9996\u9078\u9023\u63A5\u57E0: ${preferredPort}`);
|
|
88456
|
-
return preferredPort;
|
|
88473
|
+
if (this.BROWSER_UNSAFE_PORTS.has(preferredPort)) {
|
|
88474
|
+
logger.warn(`\u9996\u9078\u9023\u63A5\u57E0 ${preferredPort} \u662F\u700F\u89BD\u5668\u4E0D\u5B89\u5168\u7AEF\u53E3\uFF0C\u8DF3\u904E`);
|
|
88457
88475
|
} else {
|
|
88458
|
-
logger.
|
|
88476
|
+
logger.debug(`\u6AA2\u67E5\u9996\u9078\u9023\u63A5\u57E0: ${preferredPort}`);
|
|
88477
|
+
const available = await this.isPortAvailable(preferredPort);
|
|
88478
|
+
if (available) {
|
|
88479
|
+
logger.info(`\u4F7F\u7528\u9996\u9078\u9023\u63A5\u57E0: ${preferredPort}`);
|
|
88480
|
+
return preferredPort;
|
|
88481
|
+
} else {
|
|
88482
|
+
logger.warn(`\u9996\u9078\u9023\u63A5\u57E0 ${preferredPort} \u4E0D\u53EF\u7528\uFF0C\u5C0B\u627E\u5176\u4ED6\u9023\u63A5\u57E0...`);
|
|
88483
|
+
}
|
|
88459
88484
|
}
|
|
88460
88485
|
}
|
|
88461
88486
|
for (let port = this.PORT_RANGE_START; port <= this.PORT_RANGE_END; port++) {
|
|
88487
|
+
if (this.BROWSER_UNSAFE_PORTS.has(port)) continue;
|
|
88462
88488
|
logger.debug(`\u6AA2\u67E5\u9023\u63A5\u57E0: ${port}`);
|
|
88463
88489
|
if (await this.isPortAvailable(port)) {
|
|
88464
88490
|
logger.info(`\u627E\u5230\u53EF\u7528\u9023\u63A5\u57E0: ${port}`);
|
|
@@ -88467,6 +88493,7 @@ var PortManager = class {
|
|
|
88467
88493
|
}
|
|
88468
88494
|
for (let i = 0; i < this.MAX_RETRIES; i++) {
|
|
88469
88495
|
const randomPort = Math.floor(Math.random() * (65535 - 1024) + 1024);
|
|
88496
|
+
if (this.BROWSER_UNSAFE_PORTS.has(randomPort)) continue;
|
|
88470
88497
|
logger.debug(`\u5617\u8A66\u96A8\u6A5F\u9023\u63A5\u57E0: ${randomPort}`);
|
|
88471
88498
|
if (await this.isPortAvailable(randomPort)) {
|
|
88472
88499
|
logger.info(`\u627E\u5230\u96A8\u6A5F\u53EF\u7528\u9023\u63A5\u57E0: ${randomPort}`);
|
|
@@ -89610,6 +89637,7 @@ function getPackageVersion() {
|
|
|
89610
89637
|
init_cjs_shims();
|
|
89611
89638
|
var import_fs5 = require("fs");
|
|
89612
89639
|
var import_path5 = __toESM(require("path"), 1);
|
|
89640
|
+
var import_os3 = __toESM(require("os"), 1);
|
|
89613
89641
|
init_logger();
|
|
89614
89642
|
var DEFAULT_LOCK_FILENAME = ".user-feedback.lock";
|
|
89615
89643
|
var DEFAULT_HEALTH_CHECK_TIMEOUT = 3e3;
|
|
@@ -89619,7 +89647,8 @@ var InstanceLock = class {
|
|
|
89619
89647
|
if (this.lockFilePath) {
|
|
89620
89648
|
return this.lockFilePath;
|
|
89621
89649
|
}
|
|
89622
|
-
const
|
|
89650
|
+
const envDir = process.env["MCP_DATA_DIR"];
|
|
89651
|
+
const dataDir = envDir || import_path5.default.join(import_os3.default.homedir(), ".user-web-feedback", "data");
|
|
89623
89652
|
if (!(0, import_fs5.existsSync)(dataDir)) {
|
|
89624
89653
|
(0, import_fs5.mkdirSync)(dataDir, { recursive: true });
|
|
89625
89654
|
}
|
|
@@ -89889,6 +89918,424 @@ var SelfProbeService = class {
|
|
|
89889
89918
|
// src/server/web-server.ts
|
|
89890
89919
|
init_crypto_helper();
|
|
89891
89920
|
init_ai_service();
|
|
89921
|
+
|
|
89922
|
+
// src/utils/ai-provider-factory.ts
|
|
89923
|
+
init_cjs_shims();
|
|
89924
|
+
init_database();
|
|
89925
|
+
init_logger();
|
|
89926
|
+
|
|
89927
|
+
// src/utils/api-provider.ts
|
|
89928
|
+
init_cjs_shims();
|
|
89929
|
+
init_dist();
|
|
89930
|
+
init_database();
|
|
89931
|
+
init_logger();
|
|
89932
|
+
init_mcp_client_manager();
|
|
89933
|
+
init_prompt_aggregator2();
|
|
89934
|
+
var MAX_RETRIES2 = 3;
|
|
89935
|
+
var RETRY_DELAYS2 = [1e3, 2e3, 4e3];
|
|
89936
|
+
var cache2 = /* @__PURE__ */ new Map();
|
|
89937
|
+
var CACHE_TTL3 = 5 * 60 * 1e3;
|
|
89938
|
+
function getProviderFromUrl2(apiUrl) {
|
|
89939
|
+
if (!apiUrl) return "google";
|
|
89940
|
+
const normalizedUrl = apiUrl.toLowerCase();
|
|
89941
|
+
if (normalizedUrl.includes("api.openai.com")) return "openai";
|
|
89942
|
+
if (normalizedUrl.includes("api.anthropic.com")) return "anthropic";
|
|
89943
|
+
if (normalizedUrl.includes("generativelanguage.googleapis.com")) return "google";
|
|
89944
|
+
if (normalizedUrl.includes("nvidia.com")) return "nvidia";
|
|
89945
|
+
if (normalizedUrl.includes("bigmodel.cn") || normalizedUrl.includes("z.ai")) return "zai";
|
|
89946
|
+
return "openai";
|
|
89947
|
+
}
|
|
89948
|
+
var APIProvider = class {
|
|
89949
|
+
getName() {
|
|
89950
|
+
return "API Provider";
|
|
89951
|
+
}
|
|
89952
|
+
getMode() {
|
|
89953
|
+
return "api";
|
|
89954
|
+
}
|
|
89955
|
+
async isAvailable() {
|
|
89956
|
+
const settings = getAISettings();
|
|
89957
|
+
return !!(settings?.apiKey && settings.apiKey !== "YOUR_API_KEY_HERE");
|
|
89958
|
+
}
|
|
89959
|
+
async generateReply(request) {
|
|
89960
|
+
try {
|
|
89961
|
+
const cacheKey = `${request.aiMessage}:${request.userContext || ""}`;
|
|
89962
|
+
if (!request.toolResults) {
|
|
89963
|
+
const cached2 = cache2.get(cacheKey);
|
|
89964
|
+
if (cached2 && Date.now() - cached2.timestamp < CACHE_TTL3) {
|
|
89965
|
+
logger.debug("[APIProvider] \u4F7F\u7528\u5FEB\u53D6\u56DE\u8986");
|
|
89966
|
+
return { success: true, reply: cached2.reply, mode: "api" };
|
|
89967
|
+
}
|
|
89968
|
+
}
|
|
89969
|
+
const settings = getAISettings();
|
|
89970
|
+
if (!settings || !settings.apiKey || settings.apiKey === "YOUR_API_KEY_HERE") {
|
|
89971
|
+
logger.warn("[APIProvider] API Key \u672A\u8A2D\u5B9A\u6216\u7121\u6548");
|
|
89972
|
+
return { success: false, error: "\u8ACB\u5148\u5728\u8A2D\u5B9A\u4E2D\u914D\u7F6E AI API Key", mode: "api" };
|
|
89973
|
+
}
|
|
89974
|
+
const aggregator = getPromptAggregator();
|
|
89975
|
+
const cliSettings = getCLISettings();
|
|
89976
|
+
let mcpTools = [];
|
|
89977
|
+
if (request.includeMCPTools) {
|
|
89978
|
+
try {
|
|
89979
|
+
const allTools = mcpClientManager.getAllTools();
|
|
89980
|
+
mcpTools = allTools.map((tool) => ({
|
|
89981
|
+
name: tool.name,
|
|
89982
|
+
description: tool.description,
|
|
89983
|
+
inputSchema: tool.inputSchema
|
|
89984
|
+
}));
|
|
89985
|
+
} catch (error2) {
|
|
89986
|
+
logger.warn("[APIProvider] \u7121\u6CD5\u53D6\u5F97 MCP \u5DE5\u5177", error2);
|
|
89987
|
+
}
|
|
89988
|
+
}
|
|
89989
|
+
const context = aggregator.buildContextSync(request, settings, cliSettings, mcpTools);
|
|
89990
|
+
context.mode = "api";
|
|
89991
|
+
const aggregated = aggregator.aggregate(context);
|
|
89992
|
+
const promptSent = aggregated.fullPrompt;
|
|
89993
|
+
const provider = getProviderFromUrl2(settings.apiUrl);
|
|
89994
|
+
logger.info(`[APIProvider] \u4F7F\u7528 ${provider} \u63D0\u4F9B\u5546, apiUrl: ${settings.apiUrl || "(\u9810\u8A2D)"}`);
|
|
89995
|
+
let reply;
|
|
89996
|
+
if (provider !== "google") {
|
|
89997
|
+
reply = await this.generateWithOpenAI(
|
|
89998
|
+
settings.apiKey,
|
|
89999
|
+
settings.model,
|
|
90000
|
+
settings.apiUrl,
|
|
90001
|
+
promptSent,
|
|
90002
|
+
settings.temperature,
|
|
90003
|
+
settings.maxTokens
|
|
90004
|
+
);
|
|
90005
|
+
} else {
|
|
90006
|
+
reply = await this.generateWithGoogle(
|
|
90007
|
+
settings.apiKey,
|
|
90008
|
+
settings.model,
|
|
90009
|
+
promptSent,
|
|
90010
|
+
settings.temperature,
|
|
90011
|
+
settings.maxTokens
|
|
90012
|
+
);
|
|
90013
|
+
}
|
|
90014
|
+
if (!request.toolResults) {
|
|
90015
|
+
cache2.set(cacheKey, { reply, timestamp: Date.now() });
|
|
90016
|
+
this.cleanExpiredCache();
|
|
90017
|
+
}
|
|
90018
|
+
return { success: true, reply, promptSent, mode: "api" };
|
|
90019
|
+
} catch (error2) {
|
|
90020
|
+
logger.error("[APIProvider] \u751F\u6210\u56DE\u8986\u5931\u6557", error2);
|
|
90021
|
+
return {
|
|
90022
|
+
success: false,
|
|
90023
|
+
error: error2 instanceof Error ? error2.message : "\u672A\u77E5\u932F\u8AA4",
|
|
90024
|
+
mode: "api"
|
|
90025
|
+
};
|
|
90026
|
+
}
|
|
90027
|
+
}
|
|
90028
|
+
async generateWithGoogle(apiKey, model, prompt, temperature, maxTokens, retryCount = 0) {
|
|
90029
|
+
try {
|
|
90030
|
+
const genAI = new GoogleGenerativeAI(apiKey);
|
|
90031
|
+
const generativeModel = genAI.getGenerativeModel({
|
|
90032
|
+
model,
|
|
90033
|
+
generationConfig: {
|
|
90034
|
+
temperature: temperature ?? 0.7,
|
|
90035
|
+
maxOutputTokens: maxTokens ?? 1e3
|
|
90036
|
+
}
|
|
90037
|
+
});
|
|
90038
|
+
const result = await generativeModel.generateContent(prompt);
|
|
90039
|
+
const response = await result.response;
|
|
90040
|
+
const text = response.text();
|
|
90041
|
+
if (!text) throw new Error("AI \u56DE\u8986\u70BA\u7A7A");
|
|
90042
|
+
return text;
|
|
90043
|
+
} catch (error2) {
|
|
90044
|
+
if (error2 instanceof Error) {
|
|
90045
|
+
if (error2.message.includes("429") || error2.message.includes("quota")) {
|
|
90046
|
+
if (retryCount < MAX_RETRIES2) {
|
|
90047
|
+
await this.sleep(RETRY_DELAYS2[retryCount] || 4e3);
|
|
90048
|
+
return this.generateWithGoogle(apiKey, model, prompt, temperature, maxTokens, retryCount + 1);
|
|
90049
|
+
}
|
|
90050
|
+
throw new Error("API \u914D\u984D\u5DF2\u7528\u76E1\u6216\u901F\u7387\u9650\u5236\uFF0C\u8ACB\u7A0D\u5F8C\u518D\u8A66");
|
|
90051
|
+
}
|
|
90052
|
+
if (error2.message.includes("API key") || error2.message.includes("401")) {
|
|
90053
|
+
throw new Error("API Key \u7121\u6548\uFF0C\u8ACB\u6AA2\u67E5\u8A2D\u5B9A");
|
|
90054
|
+
}
|
|
90055
|
+
if (retryCount < MAX_RETRIES2) {
|
|
90056
|
+
await this.sleep(RETRY_DELAYS2[retryCount] || 4e3);
|
|
90057
|
+
return this.generateWithGoogle(apiKey, model, prompt, temperature, maxTokens, retryCount + 1);
|
|
90058
|
+
}
|
|
90059
|
+
}
|
|
90060
|
+
throw error2;
|
|
90061
|
+
}
|
|
90062
|
+
}
|
|
90063
|
+
async generateWithOpenAI(apiKey, model, apiUrl, prompt, temperature, maxTokens, retryCount = 0) {
|
|
90064
|
+
try {
|
|
90065
|
+
const OpenAI = (await import("openai")).default;
|
|
90066
|
+
const client = new OpenAI({
|
|
90067
|
+
apiKey,
|
|
90068
|
+
baseURL: apiUrl || "https://api.openai.com/v1"
|
|
90069
|
+
});
|
|
90070
|
+
const response = await client.chat.completions.create({
|
|
90071
|
+
model,
|
|
90072
|
+
messages: [{ role: "user", content: prompt }],
|
|
90073
|
+
temperature: temperature ?? 0.7,
|
|
90074
|
+
max_tokens: maxTokens ?? 1e3
|
|
90075
|
+
});
|
|
90076
|
+
const text = response.choices?.[0]?.message?.content;
|
|
90077
|
+
if (!text) throw new Error("AI \u56DE\u8986\u70BA\u7A7A");
|
|
90078
|
+
return text;
|
|
90079
|
+
} catch (error2) {
|
|
90080
|
+
if (error2 instanceof Error) {
|
|
90081
|
+
if (error2.message.includes("429") || error2.message.includes("quota") || error2.message.includes("rate")) {
|
|
90082
|
+
if (retryCount < MAX_RETRIES2) {
|
|
90083
|
+
await this.sleep(RETRY_DELAYS2[retryCount] || 4e3);
|
|
90084
|
+
return this.generateWithOpenAI(apiKey, model, apiUrl, prompt, temperature, maxTokens, retryCount + 1);
|
|
90085
|
+
}
|
|
90086
|
+
throw new Error("API \u914D\u984D\u5DF2\u7528\u76E1\u6216\u901F\u7387\u9650\u5236\uFF0C\u8ACB\u7A0D\u5F8C\u518D\u8A66");
|
|
90087
|
+
}
|
|
90088
|
+
if (error2.message.includes("API key") || error2.message.includes("401") || error2.message.includes("Unauthorized")) {
|
|
90089
|
+
throw new Error("API Key \u7121\u6548\uFF0C\u8ACB\u6AA2\u67E5\u8A2D\u5B9A");
|
|
90090
|
+
}
|
|
90091
|
+
if (retryCount < MAX_RETRIES2) {
|
|
90092
|
+
await this.sleep(RETRY_DELAYS2[retryCount] || 4e3);
|
|
90093
|
+
return this.generateWithOpenAI(apiKey, model, apiUrl, prompt, temperature, maxTokens, retryCount + 1);
|
|
90094
|
+
}
|
|
90095
|
+
}
|
|
90096
|
+
throw error2;
|
|
90097
|
+
}
|
|
90098
|
+
}
|
|
90099
|
+
sleep(ms) {
|
|
90100
|
+
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
90101
|
+
}
|
|
90102
|
+
cleanExpiredCache() {
|
|
90103
|
+
const now = Date.now();
|
|
90104
|
+
for (const [key, entry] of cache2.entries()) {
|
|
90105
|
+
if (now - entry.timestamp > CACHE_TTL3) cache2.delete(key);
|
|
90106
|
+
}
|
|
90107
|
+
}
|
|
90108
|
+
};
|
|
90109
|
+
|
|
90110
|
+
// src/utils/cli-provider.ts
|
|
90111
|
+
init_cjs_shims();
|
|
90112
|
+
init_database();
|
|
90113
|
+
init_logger();
|
|
90114
|
+
init_cli_executor();
|
|
90115
|
+
init_cli_detector();
|
|
90116
|
+
init_mcp_client_manager();
|
|
90117
|
+
init_prompt_aggregator2();
|
|
90118
|
+
var CLIProvider = class {
|
|
90119
|
+
cliSettings;
|
|
90120
|
+
constructor(cliSettings) {
|
|
90121
|
+
this.cliSettings = cliSettings;
|
|
90122
|
+
}
|
|
90123
|
+
getName() {
|
|
90124
|
+
return `CLI (${this.cliSettings.cliTool})`;
|
|
90125
|
+
}
|
|
90126
|
+
getMode() {
|
|
90127
|
+
return "cli";
|
|
90128
|
+
}
|
|
90129
|
+
async isAvailable() {
|
|
90130
|
+
return isToolAvailable(this.cliSettings.cliTool);
|
|
90131
|
+
}
|
|
90132
|
+
async generateReply(request) {
|
|
90133
|
+
const tool = this.cliSettings.cliTool;
|
|
90134
|
+
const available = await this.isAvailable();
|
|
90135
|
+
if (!available) {
|
|
90136
|
+
logger.warn(`[CLIProvider] CLI tool not available: ${tool}`);
|
|
90137
|
+
return {
|
|
90138
|
+
success: false,
|
|
90139
|
+
error: `CLI \u5DE5\u5177 ${tool} \u672A\u5B89\u88DD\u6216\u4E0D\u53EF\u7528`,
|
|
90140
|
+
mode: "cli",
|
|
90141
|
+
cliTool: tool
|
|
90142
|
+
};
|
|
90143
|
+
}
|
|
90144
|
+
const terminalId = `${request.projectPath || "default"}-${tool}`.replace(/[^a-zA-Z0-9-]/g, "_");
|
|
90145
|
+
let terminal = getCLITerminalById(terminalId);
|
|
90146
|
+
if (!terminal) {
|
|
90147
|
+
terminal = createCLITerminal({
|
|
90148
|
+
id: terminalId,
|
|
90149
|
+
projectName: request.projectName || "\u672A\u547D\u540D\u5C08\u6848",
|
|
90150
|
+
projectPath: request.projectPath || "",
|
|
90151
|
+
tool,
|
|
90152
|
+
status: "running",
|
|
90153
|
+
pid: void 0
|
|
90154
|
+
});
|
|
90155
|
+
} else {
|
|
90156
|
+
updateCLITerminal(terminalId, { status: "running" });
|
|
90157
|
+
}
|
|
90158
|
+
const aggregator = getPromptAggregator();
|
|
90159
|
+
const settings = getAISettings();
|
|
90160
|
+
const cliSettings = getCLISettings();
|
|
90161
|
+
let mcpTools = [];
|
|
90162
|
+
if (request.includeMCPTools) {
|
|
90163
|
+
try {
|
|
90164
|
+
const allTools = mcpClientManager.getAllTools();
|
|
90165
|
+
mcpTools = allTools.map((t) => ({
|
|
90166
|
+
name: t.name,
|
|
90167
|
+
description: t.description,
|
|
90168
|
+
inputSchema: t.inputSchema
|
|
90169
|
+
}));
|
|
90170
|
+
} catch {
|
|
90171
|
+
logger.warn("[CLIProvider] \u7121\u6CD5\u7372\u53D6 MCP \u5DE5\u5177");
|
|
90172
|
+
}
|
|
90173
|
+
}
|
|
90174
|
+
const context = aggregator.buildContextSync(request, settings ?? null, cliSettings, mcpTools);
|
|
90175
|
+
context.mode = "cli";
|
|
90176
|
+
const aggregated = aggregator.aggregate(context);
|
|
90177
|
+
const prompt = aggregated.fullPrompt;
|
|
90178
|
+
logger.info(`[CLIProvider] Prompt \u5927\u5C0F\u5206\u6790:`, {
|
|
90179
|
+
totalLength: prompt.length,
|
|
90180
|
+
totalKB: (prompt.length / 1024).toFixed(2) + " KB",
|
|
90181
|
+
mcpToolsCount: mcpTools.length,
|
|
90182
|
+
mcpToolsSize: JSON.stringify(mcpTools).length,
|
|
90183
|
+
systemPromptLength: settings?.systemPrompt?.length || 0,
|
|
90184
|
+
aiMessageLength: request.aiMessage?.length || 0,
|
|
90185
|
+
userContextLength: request.userContext?.length || 0,
|
|
90186
|
+
timeout: this.cliSettings.cliTimeout
|
|
90187
|
+
});
|
|
90188
|
+
try {
|
|
90189
|
+
const result = await executeCLI({
|
|
90190
|
+
tool,
|
|
90191
|
+
prompt,
|
|
90192
|
+
timeout: this.cliSettings.cliTimeout,
|
|
90193
|
+
workingDirectory: request.projectPath,
|
|
90194
|
+
outputFormat: "text"
|
|
90195
|
+
});
|
|
90196
|
+
insertCLIExecutionLog({
|
|
90197
|
+
terminalId,
|
|
90198
|
+
prompt: prompt.substring(0, 1e3),
|
|
90199
|
+
response: result.success ? result.output.substring(0, 5e3) : null,
|
|
90200
|
+
executionTime: result.executionTime,
|
|
90201
|
+
success: result.success,
|
|
90202
|
+
error: result.error
|
|
90203
|
+
});
|
|
90204
|
+
if (!result.success) {
|
|
90205
|
+
updateCLITerminal(terminalId, { status: "error" });
|
|
90206
|
+
return {
|
|
90207
|
+
success: false,
|
|
90208
|
+
error: result.error || "CLI \u57F7\u884C\u5931\u6557",
|
|
90209
|
+
mode: "cli",
|
|
90210
|
+
cliTool: tool,
|
|
90211
|
+
promptSent: prompt
|
|
90212
|
+
};
|
|
90213
|
+
}
|
|
90214
|
+
if (request.includeMCPTools) {
|
|
90215
|
+
const mcpHandler = createCLIMCPHandler(settings?.maxToolRounds || 10);
|
|
90216
|
+
updateCLITerminal(terminalId, { status: "mcp-processing" });
|
|
90217
|
+
const handlerResult = await mcpHandler.handleResponse(result.output, (call) => {
|
|
90218
|
+
logger.info(`[CLIProvider] \u57F7\u884C MCP \u5DE5\u5177: ${call.toolName}`);
|
|
90219
|
+
});
|
|
90220
|
+
if (handlerResult.toolCallsDetected) {
|
|
90221
|
+
insertCLIExecutionLog({
|
|
90222
|
+
terminalId,
|
|
90223
|
+
prompt: `[MCP] ${handlerResult.toolResults.length} \u500B\u5DE5\u5177\u547C\u53EB`,
|
|
90224
|
+
response: handlerResult.finalResponse.substring(0, 5e3),
|
|
90225
|
+
executionTime: handlerResult.toolResults.reduce((sum, r) => sum + r.executionTime, 0),
|
|
90226
|
+
success: true,
|
|
90227
|
+
error: void 0
|
|
90228
|
+
});
|
|
90229
|
+
}
|
|
90230
|
+
updateCLITerminal(terminalId, { status: "idle" });
|
|
90231
|
+
return {
|
|
90232
|
+
success: true,
|
|
90233
|
+
reply: handlerResult.finalResponse,
|
|
90234
|
+
mode: "cli",
|
|
90235
|
+
cliTool: tool,
|
|
90236
|
+
promptSent: prompt
|
|
90237
|
+
};
|
|
90238
|
+
}
|
|
90239
|
+
updateCLITerminal(terminalId, { status: "idle" });
|
|
90240
|
+
return {
|
|
90241
|
+
success: true,
|
|
90242
|
+
reply: result.output,
|
|
90243
|
+
mode: "cli",
|
|
90244
|
+
cliTool: tool,
|
|
90245
|
+
promptSent: prompt
|
|
90246
|
+
};
|
|
90247
|
+
} catch (error2) {
|
|
90248
|
+
insertCLIExecutionLog({
|
|
90249
|
+
terminalId,
|
|
90250
|
+
prompt: prompt.substring(0, 1e3),
|
|
90251
|
+
response: null,
|
|
90252
|
+
executionTime: 0,
|
|
90253
|
+
success: false,
|
|
90254
|
+
error: error2 instanceof Error ? error2.message : "\u672A\u77E5\u932F\u8AA4"
|
|
90255
|
+
});
|
|
90256
|
+
updateCLITerminal(terminalId, { status: "error" });
|
|
90257
|
+
return {
|
|
90258
|
+
success: false,
|
|
90259
|
+
error: error2 instanceof Error ? error2.message : "\u672A\u77E5\u932F\u8AA4",
|
|
90260
|
+
mode: "cli",
|
|
90261
|
+
cliTool: tool,
|
|
90262
|
+
promptSent: prompt
|
|
90263
|
+
};
|
|
90264
|
+
}
|
|
90265
|
+
}
|
|
90266
|
+
};
|
|
90267
|
+
|
|
90268
|
+
// src/utils/ai-provider-factory.ts
|
|
90269
|
+
var AIProviderFactory = class _AIProviderFactory {
|
|
90270
|
+
static instance;
|
|
90271
|
+
currentProvider = null;
|
|
90272
|
+
fallbackProvider = null;
|
|
90273
|
+
constructor() {
|
|
90274
|
+
}
|
|
90275
|
+
/**
|
|
90276
|
+
* 取得工廠實例 (Singleton)
|
|
90277
|
+
*/
|
|
90278
|
+
static getInstance() {
|
|
90279
|
+
if (!_AIProviderFactory.instance) {
|
|
90280
|
+
_AIProviderFactory.instance = new _AIProviderFactory();
|
|
90281
|
+
}
|
|
90282
|
+
return _AIProviderFactory.instance;
|
|
90283
|
+
}
|
|
90284
|
+
/**
|
|
90285
|
+
* 根據設定取得適當的 Provider
|
|
90286
|
+
*/
|
|
90287
|
+
async getProvider() {
|
|
90288
|
+
const cliSettings = getCLISettings();
|
|
90289
|
+
const mode = cliSettings?.aiMode === "cli" ? "cli" : "api";
|
|
90290
|
+
logger.debug("[AIProviderFactory] \u53D6\u5F97 Provider", { mode, cliTool: cliSettings?.cliTool });
|
|
90291
|
+
if (mode === "cli") {
|
|
90292
|
+
const cliProvider = new CLIProvider(cliSettings);
|
|
90293
|
+
const available = await cliProvider.isAvailable();
|
|
90294
|
+
if (!available) {
|
|
90295
|
+
logger.warn("[AIProviderFactory] CLI Provider \u4E0D\u53EF\u7528");
|
|
90296
|
+
if (cliSettings?.cliFallbackToApi) {
|
|
90297
|
+
logger.info("[AIProviderFactory] \u56DE\u9000\u5230 API Provider");
|
|
90298
|
+
return new APIProvider();
|
|
90299
|
+
}
|
|
90300
|
+
throw new Error(`CLI \u5DE5\u5177 ${cliSettings?.cliTool} \u4E0D\u53EF\u7528\uFF0C\u4E14\u672A\u555F\u7528 API \u56DE\u9000`);
|
|
90301
|
+
}
|
|
90302
|
+
return cliProvider;
|
|
90303
|
+
}
|
|
90304
|
+
return new APIProvider();
|
|
90305
|
+
}
|
|
90306
|
+
/**
|
|
90307
|
+
* 取得當前模式
|
|
90308
|
+
*/
|
|
90309
|
+
getCurrentMode() {
|
|
90310
|
+
const cliSettings = getCLISettings();
|
|
90311
|
+
return cliSettings?.aiMode === "cli" ? "cli" : "api";
|
|
90312
|
+
}
|
|
90313
|
+
/**
|
|
90314
|
+
* 取得當前 CLI 工具名稱
|
|
90315
|
+
*/
|
|
90316
|
+
getCurrentCLITool() {
|
|
90317
|
+
const cliSettings = getCLISettings();
|
|
90318
|
+
return cliSettings?.cliTool;
|
|
90319
|
+
}
|
|
90320
|
+
/**
|
|
90321
|
+
* 使用工廠生成 AI 回覆
|
|
90322
|
+
*/
|
|
90323
|
+
async generateReply(request) {
|
|
90324
|
+
try {
|
|
90325
|
+
const provider = await this.getProvider();
|
|
90326
|
+
logger.info(`[AIProviderFactory] \u4F7F\u7528 ${provider.getName()} \u751F\u6210\u56DE\u8986`);
|
|
90327
|
+
return provider.generateReply(request);
|
|
90328
|
+
} catch (error2) {
|
|
90329
|
+
logger.error("[AIProviderFactory] \u751F\u6210\u56DE\u8986\u5931\u6557", error2);
|
|
90330
|
+
return {
|
|
90331
|
+
success: false,
|
|
90332
|
+
error: error2 instanceof Error ? error2.message : "\u672A\u77E5\u932F\u8AA4"
|
|
90333
|
+
};
|
|
90334
|
+
}
|
|
90335
|
+
}
|
|
90336
|
+
};
|
|
90337
|
+
|
|
90338
|
+
// src/server/web-server.ts
|
|
89892
90339
|
init_mcp_client_manager();
|
|
89893
90340
|
init_cli_detector();
|
|
89894
90341
|
|
|
@@ -90927,8 +91374,9 @@ var WebServer = class {
|
|
|
90927
91374
|
});
|
|
90928
91375
|
return;
|
|
90929
91376
|
}
|
|
90930
|
-
logger.info("\u958B\u59CB\u751F\u6210 AI \u56DE\u8986");
|
|
90931
|
-
const
|
|
91377
|
+
logger.info("\u958B\u59CB\u751F\u6210 AI \u56DE\u8986 (AIProviderFactory)");
|
|
91378
|
+
const factory = AIProviderFactory.getInstance();
|
|
91379
|
+
const result = await factory.generateReply(data);
|
|
90932
91380
|
if (result.success) {
|
|
90933
91381
|
logger.info("AI \u56DE\u8986\u751F\u6210\u6210\u529F");
|
|
90934
91382
|
} else {
|
|
@@ -92306,7 +92754,8 @@ var WebServer = class {
|
|
|
92306
92754
|
const autoReplyTimer = setTimeout(async () => {
|
|
92307
92755
|
logger.info(`\u89F8\u767C\u81EA\u52D5\u56DE\u8986: \u6703\u8A71 ${sessionId}`);
|
|
92308
92756
|
try {
|
|
92309
|
-
const
|
|
92757
|
+
const factory = AIProviderFactory.getInstance();
|
|
92758
|
+
const result = await factory.generateReply({
|
|
92310
92759
|
aiMessage: workSummary,
|
|
92311
92760
|
userContext: "\u4F7F\u7528\u8005\u672A\u5728\u6642\u9593\u5167\u56DE\u61C9\uFF0C\u7CFB\u7D71\u81EA\u52D5\u751F\u6210\u56DE\u8986"
|
|
92312
92761
|
});
|