reasonix 0.51.0 → 0.52.0

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 (143) hide show
  1. package/dashboard/dist/app.css +1 -1
  2. package/dashboard/dist/app.js +3 -3
  3. package/dashboard/dist/app.js.map +1 -1
  4. package/dist/cli/{acp-XEUHGG7X.js → acp-NEUYWGUU.js} +26 -26
  5. package/dist/cli/chat-QA6IVFJD.js +49 -0
  6. package/dist/cli/{chunk-HGK57NBN.js → chunk-2W4F3RIZ.js} +2 -2
  7. package/dist/cli/{chunk-UO6E7FN3.js → chunk-3OXD5CBM.js} +32756 -31192
  8. package/dist/cli/chunk-3OXD5CBM.js.map +1 -0
  9. package/dist/cli/{chunk-UMZ6KHTS.js → chunk-5YLEKX2V.js} +2 -2
  10. package/dist/cli/{chunk-BA5R6BAE.js → chunk-6QBUXA73.js} +2 -2
  11. package/dist/cli/chunk-77JIQ7SL.js +40 -0
  12. package/dist/cli/chunk-77JIQ7SL.js.map +1 -0
  13. package/dist/cli/{chunk-6XWXIVQ3.js → chunk-AMSK3ZLB.js} +2 -2
  14. package/dist/cli/chunk-AMSK3ZLB.js.map +1 -0
  15. package/dist/cli/{chunk-A5PBEIJ7.js → chunk-AOYUW3HR.js} +37 -4
  16. package/dist/cli/chunk-AOYUW3HR.js.map +1 -0
  17. package/dist/cli/{chunk-3YRTIWFX.js → chunk-ARBGTNHM.js} +2 -2
  18. package/dist/cli/{chunk-3BTK5BHI.js → chunk-B4MOGWHW.js} +2 -2
  19. package/dist/cli/{chunk-5AIDYVH2.js → chunk-CFJY64UA.js} +2 -2
  20. package/dist/cli/{chunk-SBHF5NWD.js → chunk-CGVW5W7N.js} +14 -14
  21. package/dist/cli/{chunk-SBHF5NWD.js.map → chunk-CGVW5W7N.js.map} +1 -1
  22. package/dist/cli/{chunk-DVD67FXQ.js → chunk-CLHMV6OL.js} +568 -66
  23. package/dist/cli/chunk-CLHMV6OL.js.map +1 -0
  24. package/dist/cli/{chunk-2WUEAI2I.js → chunk-CPCUMMSR.js} +3 -3
  25. package/dist/cli/{chunk-JHWQDJZA.js → chunk-CTRM32BP.js} +2 -2
  26. package/dist/cli/{chunk-544J4PXD.js → chunk-D6WRFR6V.js} +5 -5
  27. package/dist/cli/{chunk-N4SEBLU4.js → chunk-DLTE4GBY.js} +3 -3
  28. package/dist/cli/{chunk-NRROJXXT.js → chunk-FY5UERSG.js} +9 -9
  29. package/dist/cli/{chunk-C2MRSJTV.js → chunk-GFJJEW3Z.js} +18 -10
  30. package/dist/cli/chunk-GFJJEW3Z.js.map +1 -0
  31. package/dist/cli/{chunk-R6KIHEF3.js → chunk-GNRKXRRE.js} +743 -660
  32. package/dist/cli/chunk-GNRKXRRE.js.map +1 -0
  33. package/dist/cli/{chunk-SXSAWOB7.js → chunk-HI6THNAZ.js} +19 -17
  34. package/dist/cli/chunk-HI6THNAZ.js.map +1 -0
  35. package/dist/cli/{chunk-K4YQFULP.js → chunk-HNZ4727T.js} +15 -15
  36. package/dist/cli/chunk-I3NE5S63.js +54 -0
  37. package/dist/cli/{chunk-EAMXOWUW.js.map → chunk-I3NE5S63.js.map} +1 -1
  38. package/dist/cli/{chunk-FEZK652I.js → chunk-MVLPXZAA.js} +834 -10
  39. package/dist/cli/chunk-MVLPXZAA.js.map +1 -0
  40. package/dist/cli/{chunk-36BM7INR.js → chunk-MW64SQUE.js} +2 -2
  41. package/dist/cli/{chunk-Z3MKG7MQ.js → chunk-OMNRXZNA.js} +2 -2
  42. package/dist/cli/{chunk-7YPMTE3U.js → chunk-RCC73DWQ.js} +5 -9
  43. package/dist/cli/chunk-RCC73DWQ.js.map +1 -0
  44. package/dist/cli/{chunk-2HVTBFCI.js → chunk-RHQOGG43.js} +5 -3
  45. package/dist/cli/chunk-RHQOGG43.js.map +1 -0
  46. package/dist/cli/{chunk-EWVFGYT6.js → chunk-VVPV5HU6.js} +2 -2
  47. package/dist/cli/{chunk-7YB26OQO.js → chunk-WPY7AFS6.js} +2 -2
  48. package/dist/cli/{chunk-BM6BBFAV.js → chunk-XBYHNZ5Z.js} +2 -2
  49. package/dist/cli/{chunk-WPOKBW5E.js → chunk-XNMXVL6C.js} +2 -2
  50. package/dist/cli/{chunk-SVD4UPRQ.js → chunk-XUZHBQSM.js} +2 -2
  51. package/dist/cli/{chunk-Q46B3Z7H.js → chunk-YMYX6QTC.js} +8 -5
  52. package/dist/cli/{chunk-Q46B3Z7H.js.map → chunk-YMYX6QTC.js.map} +1 -1
  53. package/dist/cli/{chunk-K3QJ3GKI.js → chunk-Z663GVUB.js} +3 -3
  54. package/dist/cli/{code-BMXLBC7D.js → code-WN6D4VZO.js} +35 -36
  55. package/dist/cli/{code-BMXLBC7D.js.map → code-WN6D4VZO.js.map} +1 -1
  56. package/dist/cli/{commands-E4RZXMF6.js → commands-DHETOY7O.js} +4 -4
  57. package/dist/cli/{commit-KSRQ64IL.js → commit-BBUYAKZV.js} +3 -3
  58. package/dist/cli/{config-QNDONOTU.js → config-KV7VV5LG.js} +4 -2
  59. package/dist/cli/{desktop-H3ZHIMDA.js → desktop-LJVXWXNF.js} +557 -70
  60. package/dist/cli/desktop-LJVXWXNF.js.map +1 -0
  61. package/dist/cli/diff-2JHMQAHI.js +165 -0
  62. package/dist/cli/{diff-I4PYI43W.js.map → diff-2JHMQAHI.js.map} +1 -1
  63. package/dist/cli/{doctor-Y2E4MY2F.js → doctor-GI5LOEZL.js} +11 -11
  64. package/dist/cli/{events-47HOT7ZA.js → events-LBKMLFM4.js} +5 -5
  65. package/dist/cli/index.js +40 -39
  66. package/dist/cli/index.js.map +1 -1
  67. package/dist/cli/{mcp-76DK63ZB.js → mcp-DKEJK5ND.js} +3 -3
  68. package/dist/cli/{mcp-browse-SDNUGO74.js → mcp-browse-V7KWDY32.js} +15 -15
  69. package/dist/cli/{mcp-browse-SDNUGO74.js.map → mcp-browse-V7KWDY32.js.map} +1 -1
  70. package/dist/cli/{mcp-inspect-BL5DEO5M.js → mcp-inspect-MTABNHVM.js} +5 -5
  71. package/dist/cli/{prompt-JLATI3P7.js → prompt-ATI7DKHF.js} +5 -5
  72. package/dist/cli/{prune-sessions-WHZDFUKD.js → prune-sessions-YQQSZTZS.js} +4 -4
  73. package/dist/cli/{replay-MHXS7C7Z.js → replay-ZJQ4I4CJ.js} +30 -30
  74. package/dist/cli/{replay-MHXS7C7Z.js.map → replay-ZJQ4I4CJ.js.map} +1 -1
  75. package/dist/cli/{run-SXNCPRJE.js → run-HFPRNWJY.js} +22 -22
  76. package/dist/cli/{server-GEHOE6CO.js → server-UHKO2VVM.js} +23 -23
  77. package/dist/cli/{sessions-EPBFYISL.js → sessions-IQEWWUH3.js} +16 -16
  78. package/dist/cli/setup-5BYKCL62.js +502 -0
  79. package/dist/cli/setup-5BYKCL62.js.map +1 -0
  80. package/dist/cli/{stats-4WB4XHBP.js → stats-OFCGOQMZ.js} +6 -6
  81. package/dist/cli/{version-4SP3DLLH.js → version-EODUFAAI.js} +16 -16
  82. package/dist/index.d.ts +12 -1
  83. package/dist/index.js +613 -78
  84. package/dist/index.js.map +1 -1
  85. package/package.json +21 -3
  86. package/dist/cli/chat-NJ2Q5KHG.js +0 -50
  87. package/dist/cli/chunk-2HVTBFCI.js.map +0 -1
  88. package/dist/cli/chunk-5BBC6YMV.js +0 -832
  89. package/dist/cli/chunk-5BBC6YMV.js.map +0 -1
  90. package/dist/cli/chunk-6XWXIVQ3.js.map +0 -1
  91. package/dist/cli/chunk-7YPMTE3U.js.map +0 -1
  92. package/dist/cli/chunk-A5PBEIJ7.js.map +0 -1
  93. package/dist/cli/chunk-C2MRSJTV.js.map +0 -1
  94. package/dist/cli/chunk-DVD67FXQ.js.map +0 -1
  95. package/dist/cli/chunk-EAMXOWUW.js +0 -54
  96. package/dist/cli/chunk-FEZK652I.js.map +0 -1
  97. package/dist/cli/chunk-R6KIHEF3.js.map +0 -1
  98. package/dist/cli/chunk-SXSAWOB7.js.map +0 -1
  99. package/dist/cli/chunk-UO6E7FN3.js.map +0 -1
  100. package/dist/cli/chunk-UPW544V3.js +0 -96
  101. package/dist/cli/chunk-UPW544V3.js.map +0 -1
  102. package/dist/cli/desktop-H3ZHIMDA.js.map +0 -1
  103. package/dist/cli/devtools-HW3WDT3Q.js +0 -91
  104. package/dist/cli/devtools-HW3WDT3Q.js.map +0 -1
  105. package/dist/cli/diff-I4PYI43W.js +0 -165
  106. package/dist/cli/setup-IW2XR5XI.js +0 -593
  107. package/dist/cli/setup-IW2XR5XI.js.map +0 -1
  108. /package/dist/cli/{acp-XEUHGG7X.js.map → acp-NEUYWGUU.js.map} +0 -0
  109. /package/dist/cli/{chat-NJ2Q5KHG.js.map → chat-QA6IVFJD.js.map} +0 -0
  110. /package/dist/cli/{chunk-HGK57NBN.js.map → chunk-2W4F3RIZ.js.map} +0 -0
  111. /package/dist/cli/{chunk-UMZ6KHTS.js.map → chunk-5YLEKX2V.js.map} +0 -0
  112. /package/dist/cli/{chunk-BA5R6BAE.js.map → chunk-6QBUXA73.js.map} +0 -0
  113. /package/dist/cli/{chunk-3YRTIWFX.js.map → chunk-ARBGTNHM.js.map} +0 -0
  114. /package/dist/cli/{chunk-3BTK5BHI.js.map → chunk-B4MOGWHW.js.map} +0 -0
  115. /package/dist/cli/{chunk-5AIDYVH2.js.map → chunk-CFJY64UA.js.map} +0 -0
  116. /package/dist/cli/{chunk-2WUEAI2I.js.map → chunk-CPCUMMSR.js.map} +0 -0
  117. /package/dist/cli/{chunk-JHWQDJZA.js.map → chunk-CTRM32BP.js.map} +0 -0
  118. /package/dist/cli/{chunk-544J4PXD.js.map → chunk-D6WRFR6V.js.map} +0 -0
  119. /package/dist/cli/{chunk-N4SEBLU4.js.map → chunk-DLTE4GBY.js.map} +0 -0
  120. /package/dist/cli/{chunk-NRROJXXT.js.map → chunk-FY5UERSG.js.map} +0 -0
  121. /package/dist/cli/{chunk-K4YQFULP.js.map → chunk-HNZ4727T.js.map} +0 -0
  122. /package/dist/cli/{chunk-36BM7INR.js.map → chunk-MW64SQUE.js.map} +0 -0
  123. /package/dist/cli/{chunk-Z3MKG7MQ.js.map → chunk-OMNRXZNA.js.map} +0 -0
  124. /package/dist/cli/{chunk-EWVFGYT6.js.map → chunk-VVPV5HU6.js.map} +0 -0
  125. /package/dist/cli/{chunk-7YB26OQO.js.map → chunk-WPY7AFS6.js.map} +0 -0
  126. /package/dist/cli/{chunk-BM6BBFAV.js.map → chunk-XBYHNZ5Z.js.map} +0 -0
  127. /package/dist/cli/{chunk-WPOKBW5E.js.map → chunk-XNMXVL6C.js.map} +0 -0
  128. /package/dist/cli/{chunk-SVD4UPRQ.js.map → chunk-XUZHBQSM.js.map} +0 -0
  129. /package/dist/cli/{chunk-K3QJ3GKI.js.map → chunk-Z663GVUB.js.map} +0 -0
  130. /package/dist/cli/{commands-E4RZXMF6.js.map → commands-DHETOY7O.js.map} +0 -0
  131. /package/dist/cli/{commit-KSRQ64IL.js.map → commit-BBUYAKZV.js.map} +0 -0
  132. /package/dist/cli/{config-QNDONOTU.js.map → config-KV7VV5LG.js.map} +0 -0
  133. /package/dist/cli/{doctor-Y2E4MY2F.js.map → doctor-GI5LOEZL.js.map} +0 -0
  134. /package/dist/cli/{events-47HOT7ZA.js.map → events-LBKMLFM4.js.map} +0 -0
  135. /package/dist/cli/{mcp-76DK63ZB.js.map → mcp-DKEJK5ND.js.map} +0 -0
  136. /package/dist/cli/{mcp-inspect-BL5DEO5M.js.map → mcp-inspect-MTABNHVM.js.map} +0 -0
  137. /package/dist/cli/{prompt-JLATI3P7.js.map → prompt-ATI7DKHF.js.map} +0 -0
  138. /package/dist/cli/{prune-sessions-WHZDFUKD.js.map → prune-sessions-YQQSZTZS.js.map} +0 -0
  139. /package/dist/cli/{run-SXNCPRJE.js.map → run-HFPRNWJY.js.map} +0 -0
  140. /package/dist/cli/{server-GEHOE6CO.js.map → server-UHKO2VVM.js.map} +0 -0
  141. /package/dist/cli/{sessions-EPBFYISL.js.map → sessions-IQEWWUH3.js.map} +0 -0
  142. /package/dist/cli/{stats-4WB4XHBP.js.map → stats-OFCGOQMZ.js.map} +0 -0
  143. /package/dist/cli/{version-4SP3DLLH.js.map → version-EODUFAAI.js.map} +0 -0
@@ -1,5 +1,39 @@
1
1
  #!/usr/bin/env node
2
2
  import { createRequire as __cr } from 'node:module'; if (typeof globalThis.require === 'undefined') { globalThis.require = __cr(import.meta.url); }
3
+ import {
4
+ formatMcpLifecycleEvent,
5
+ formatMcpSlowToast
6
+ } from "./chunk-CFJY64UA.js";
7
+ import {
8
+ buildTransportFromSpec,
9
+ preflightStdioSpec
10
+ } from "./chunk-XUZHBQSM.js";
11
+ import {
12
+ bridgeMcpTools
13
+ } from "./chunk-HNZ4727T.js";
14
+ import {
15
+ McpClient,
16
+ inspectMcpServer
17
+ } from "./chunk-RCC73DWQ.js";
18
+ import {
19
+ loadDotenv
20
+ } from "./chunk-2UQP6H6T.js";
21
+ import {
22
+ t
23
+ } from "./chunk-CLHMV6OL.js";
24
+ import {
25
+ decideQQAccess,
26
+ describeQQAccess,
27
+ loadQQConfig,
28
+ normalizeMcpConfig,
29
+ normalizeQQAllowlist,
30
+ normalizeQQOpenId,
31
+ overlayMatchedSpec,
32
+ parseMcpSpec,
33
+ readConfig,
34
+ redactQQOpenId,
35
+ specToRaw
36
+ } from "./chunk-AOYUW3HR.js";
3
37
  import {
4
38
  __commonJS,
5
39
  __require,
@@ -2209,14 +2243,14 @@ var require_extension = __commonJS({
2209
2243
  var require_websocket = __commonJS({
2210
2244
  "node_modules/ws/lib/websocket.js"(exports, module) {
2211
2245
  "use strict";
2212
- var EventEmitter = __require("events");
2246
+ var EventEmitter2 = __require("events");
2213
2247
  var https = __require("https");
2214
2248
  var http = __require("http");
2215
2249
  var net = __require("net");
2216
2250
  var tls = __require("tls");
2217
2251
  var { randomBytes, createHash } = __require("crypto");
2218
2252
  var { Duplex, Readable } = __require("stream");
2219
- var { URL } = __require("url");
2253
+ var { URL: URL2 } = __require("url");
2220
2254
  var PerMessageDeflate2 = require_permessage_deflate();
2221
2255
  var Receiver2 = require_receiver();
2222
2256
  var Sender2 = require_sender();
@@ -2241,7 +2275,7 @@ var require_websocket = __commonJS({
2241
2275
  var protocolVersions = [8, 13];
2242
2276
  var readyStates = ["CONNECTING", "OPEN", "CLOSING", "CLOSED"];
2243
2277
  var subprotocolRegex = /^[!#$%&'*+\-.0-9A-Z^_`|a-z~]+$/;
2244
- var WebSocket2 = class _WebSocket extends EventEmitter {
2278
+ var WebSocket2 = class _WebSocket extends EventEmitter2 {
2245
2279
  /**
2246
2280
  * Create a new `WebSocket`.
2247
2281
  *
@@ -2709,11 +2743,11 @@ var require_websocket = __commonJS({
2709
2743
  );
2710
2744
  }
2711
2745
  let parsedUrl;
2712
- if (address instanceof URL) {
2746
+ if (address instanceof URL2) {
2713
2747
  parsedUrl = address;
2714
2748
  } else {
2715
2749
  try {
2716
- parsedUrl = new URL(address);
2750
+ parsedUrl = new URL2(address);
2717
2751
  } catch {
2718
2752
  throw new SyntaxError(`Invalid URL: ${address}`);
2719
2753
  }
@@ -2850,7 +2884,7 @@ var require_websocket = __commonJS({
2850
2884
  req.abort();
2851
2885
  let addr;
2852
2886
  try {
2853
- addr = new URL(location, address);
2887
+ addr = new URL2(location, address);
2854
2888
  } catch (e) {
2855
2889
  const err = new SyntaxError(`Invalid URL: ${location}`);
2856
2890
  emitErrorAndClose(websocket, err);
@@ -3238,7 +3272,7 @@ var require_subprotocol = __commonJS({
3238
3272
  var require_websocket_server = __commonJS({
3239
3273
  "node_modules/ws/lib/websocket-server.js"(exports, module) {
3240
3274
  "use strict";
3241
- var EventEmitter = __require("events");
3275
+ var EventEmitter2 = __require("events");
3242
3276
  var http = __require("http");
3243
3277
  var { Duplex } = __require("stream");
3244
3278
  var { createHash } = __require("crypto");
@@ -3251,7 +3285,7 @@ var require_websocket_server = __commonJS({
3251
3285
  var RUNNING = 0;
3252
3286
  var CLOSING = 1;
3253
3287
  var CLOSED = 2;
3254
- var WebSocketServer2 = class extends EventEmitter {
3288
+ var WebSocketServer2 = class extends EventEmitter2 {
3255
3289
  /**
3256
3290
  * Create a `WebSocketServer` instance.
3257
3291
  *
@@ -3627,6 +3661,14 @@ var require_websocket_server = __commonJS({
3627
3661
  }
3628
3662
  });
3629
3663
 
3664
+ // src/qq/channel.ts
3665
+ import { mkdirSync, readFileSync, unlinkSync, writeFileSync } from "fs";
3666
+ import { homedir } from "os";
3667
+ import { dirname, join } from "path";
3668
+
3669
+ // src/qq/bot.ts
3670
+ import { EventEmitter } from "events";
3671
+
3630
3672
  // node_modules/ws/wrapper.mjs
3631
3673
  var import_stream = __toESM(require_stream(), 1);
3632
3674
  var import_extension = __toESM(require_extension(), 1);
@@ -3638,7 +3680,789 @@ var import_websocket = __toESM(require_websocket(), 1);
3638
3680
  var import_websocket_server = __toESM(require_websocket_server(), 1);
3639
3681
  var wrapper_default = import_websocket.default;
3640
3682
 
3683
+ // src/qq/bot.ts
3684
+ var TOKEN_URL = "https://bots.qq.com/app/getAppAccessToken";
3685
+ var BASE_URL = "https://api.sgroup.qq.com";
3686
+ var SANDBOX_URL = "https://sandbox.api.sgroup.qq.com";
3687
+ var INTENT_C2C_GROUP = 1 << 25;
3688
+ var MIN_HEARTBEAT_INTERVAL_MS = 5e3;
3689
+ var MAX_HEARTBEAT_INTERVAL_MS = 6e4;
3690
+ var ALLOWED_GATEWAY_HOSTS = ["api.sgroup.qq.com", "sandbox.api.sgroup.qq.com", "qq.com"];
3691
+ var QQBot = class extends EventEmitter {
3692
+ config;
3693
+ token = "";
3694
+ tokenExpiresAt = 0;
3695
+ ws = null;
3696
+ heartbeatTimer = null;
3697
+ seq = 0;
3698
+ sessionId = "";
3699
+ closed = false;
3700
+ readyReceived = false;
3701
+ constructor(config) {
3702
+ super();
3703
+ this.config = config;
3704
+ }
3705
+ get baseUrl() {
3706
+ return this.config.sandbox ? SANDBOX_URL : BASE_URL;
3707
+ }
3708
+ sanitizeHeartbeatInterval(interval) {
3709
+ if (typeof interval !== "number" || !Number.isFinite(interval)) {
3710
+ return null;
3711
+ }
3712
+ if (interval < MIN_HEARTBEAT_INTERVAL_MS) {
3713
+ return MIN_HEARTBEAT_INTERVAL_MS;
3714
+ }
3715
+ if (interval > MAX_HEARTBEAT_INTERVAL_MS) {
3716
+ return MAX_HEARTBEAT_INTERVAL_MS;
3717
+ }
3718
+ return Math.trunc(interval);
3719
+ }
3720
+ validateGatewayUrl(rawUrl) {
3721
+ const url = new URL(rawUrl);
3722
+ const trustedHost = ALLOWED_GATEWAY_HOSTS.some(
3723
+ (host) => url.hostname === host || url.hostname.endsWith(`.${host}`)
3724
+ );
3725
+ if (url.protocol !== "wss:" || !trustedHost || url.username || url.password || url.search || url.hash) {
3726
+ throw new Error(`Unexpected QQ gateway URL: ${rawUrl}`);
3727
+ }
3728
+ return url.toString();
3729
+ }
3730
+ async ensureToken() {
3731
+ if (this.token && Date.now() < this.tokenExpiresAt - 6e4) {
3732
+ return this.token;
3733
+ }
3734
+ const res = await fetch(TOKEN_URL, {
3735
+ method: "POST",
3736
+ headers: { "Content-Type": "application/json" },
3737
+ body: JSON.stringify({
3738
+ appId: this.config.appid,
3739
+ clientSecret: this.config.secret
3740
+ })
3741
+ });
3742
+ if (!res.ok) {
3743
+ const text = await res.text();
3744
+ throw new Error(`Failed to get access token (${res.status}): ${text}`);
3745
+ }
3746
+ const data = await res.json();
3747
+ this.token = data.access_token;
3748
+ this.tokenExpiresAt = Date.now() + data.expires_in * 1e3;
3749
+ return this.token;
3750
+ }
3751
+ async getGateway() {
3752
+ const token = await this.ensureToken();
3753
+ const res = await fetch(`${this.baseUrl}/gateway`, {
3754
+ headers: { Authorization: `QQBot ${token}` }
3755
+ });
3756
+ if (!res.ok) {
3757
+ const text = await res.text();
3758
+ throw new Error(`Failed to get gateway (${res.status}): ${text}`);
3759
+ }
3760
+ const data = await res.json();
3761
+ return this.validateGatewayUrl(data.url);
3762
+ }
3763
+ sendOp(op, data) {
3764
+ if (!this.ws) return;
3765
+ this.ws.send(JSON.stringify({ op, d: data ?? {} }));
3766
+ }
3767
+ async handlePayload(payload) {
3768
+ switch (payload.op) {
3769
+ case 10: {
3770
+ const d = payload.d;
3771
+ this.sendOp(2, {
3772
+ token: `QQBot ${await this.ensureToken()}`,
3773
+ intents: INTENT_C2C_GROUP,
3774
+ shard: [0, 1]
3775
+ });
3776
+ const heartbeatInterval = this.sanitizeHeartbeatInterval(d?.heartbeat_interval);
3777
+ if (heartbeatInterval) {
3778
+ this.heartbeatTimer = setInterval(() => {
3779
+ this.sendOp(1, this.seq || null);
3780
+ }, heartbeatInterval);
3781
+ }
3782
+ break;
3783
+ }
3784
+ case 0: {
3785
+ if (payload.s) this.seq = payload.s;
3786
+ if (payload.t === "READY") {
3787
+ const d = payload.d;
3788
+ this.sessionId = d.session_id;
3789
+ this.readyReceived = true;
3790
+ this.emit("online");
3791
+ } else if (payload.t === "C2C_MESSAGE_CREATE") {
3792
+ this.emit("message.private", payload.d);
3793
+ } else if (payload.t === "GROUP_AT_MESSAGE_CREATE") {
3794
+ this.emit("message.group", payload.d);
3795
+ }
3796
+ break;
3797
+ }
3798
+ case 7: {
3799
+ this.reconnect();
3800
+ break;
3801
+ }
3802
+ case 9: {
3803
+ this.sessionId = "";
3804
+ this.sendOp(2, {
3805
+ token: `QQBot ${await this.ensureToken()}`,
3806
+ intents: INTENT_C2C_GROUP,
3807
+ shard: [0, 1]
3808
+ });
3809
+ break;
3810
+ }
3811
+ }
3812
+ }
3813
+ async reconnect() {
3814
+ this.cleanup();
3815
+ await this.connect();
3816
+ }
3817
+ cleanup() {
3818
+ if (this.heartbeatTimer) {
3819
+ clearInterval(this.heartbeatTimer);
3820
+ this.heartbeatTimer = null;
3821
+ }
3822
+ if (this.ws) {
3823
+ this.ws.removeAllListeners();
3824
+ this.ws.close();
3825
+ this.ws = null;
3826
+ }
3827
+ }
3828
+ async connect() {
3829
+ const gatewayUrl = await this.getGateway();
3830
+ const token = await this.ensureToken();
3831
+ this.ws = new wrapper_default(gatewayUrl, {
3832
+ headers: {
3833
+ Authorization: `QQBot ${token}`,
3834
+ "X-Union-Appid": this.config.appid
3835
+ }
3836
+ });
3837
+ this.ws.on("open", () => {
3838
+ if (this.sessionId) {
3839
+ this.sendOp(6, {
3840
+ token: `QQBot ${this.token}`,
3841
+ session_id: this.sessionId,
3842
+ seq: this.seq
3843
+ });
3844
+ }
3845
+ });
3846
+ this.ws.on("message", (raw) => {
3847
+ try {
3848
+ const payload = JSON.parse(raw.toString());
3849
+ this.handlePayload(payload).catch(() => {
3850
+ });
3851
+ } catch {
3852
+ }
3853
+ });
3854
+ this.ws.on("close", () => {
3855
+ if (!this.closed) {
3856
+ if (this.readyReceived) {
3857
+ console.error("QQ WebSocket reconnecting...");
3858
+ this.cleanup();
3859
+ setTimeout(() => this.reconnect(), 3e3);
3860
+ } else {
3861
+ const msg = "QQ WebSocket closed before authentication completed \u2014 check your appId and appSecret";
3862
+ this.emit("bot_error", msg);
3863
+ this.closed = true;
3864
+ }
3865
+ }
3866
+ });
3867
+ this.ws.on("error", (err) => {
3868
+ const msg = `QQ WebSocket error: ${err.message}`;
3869
+ console.error(msg);
3870
+ this.emit("bot_error", msg);
3871
+ });
3872
+ }
3873
+ async start() {
3874
+ this.closed = false;
3875
+ this.readyReceived = false;
3876
+ await this.connect();
3877
+ }
3878
+ async stop() {
3879
+ this.closed = true;
3880
+ this.cleanup();
3881
+ }
3882
+ async sendPrivateMessage(openid, content, msgId, msgSeq) {
3883
+ const token = await this.ensureToken();
3884
+ const body = {
3885
+ content,
3886
+ msg_type: 0
3887
+ };
3888
+ if (msgId) body.msg_id = msgId;
3889
+ if (typeof msgSeq === "number" && Number.isFinite(msgSeq)) body.msg_seq = Math.trunc(msgSeq);
3890
+ const res = await fetch(`${this.baseUrl}/v2/users/${encodeURIComponent(openid)}/messages`, {
3891
+ method: "POST",
3892
+ headers: {
3893
+ Authorization: `QQBot ${token}`,
3894
+ "Content-Type": "application/json",
3895
+ "X-Union-Appid": this.config.appid
3896
+ },
3897
+ body: JSON.stringify(body)
3898
+ });
3899
+ if (!res.ok) {
3900
+ const text = await res.text();
3901
+ const msg = `QQ sendPrivateMessage failed (${res.status}): ${text}`;
3902
+ throw new Error(msg);
3903
+ }
3904
+ }
3905
+ };
3906
+
3907
+ // src/qq/strings.ts
3908
+ function formatQQModeLabel(codeMode) {
3909
+ return t(codeMode ? "handlers.qq.modeCode" : "handlers.qq.modeChat");
3910
+ }
3911
+ function formatQQAccessSummary(config) {
3912
+ const ownerOpenId = normalizeQQOpenId(config.ownerOpenId);
3913
+ const allowlist = normalizeQQAllowlist(config.allowlist) ?? [];
3914
+ const runtimeBoundOpenId = normalizeQQOpenId(config.runtimeBoundOpenId);
3915
+ if (ownerOpenId) {
3916
+ if (allowlist.length > 0) {
3917
+ return t("handlers.qq.accessOwnerWithAllowlist", {
3918
+ owner: redactQQOpenId(ownerOpenId),
3919
+ count: allowlist.length
3920
+ });
3921
+ }
3922
+ return t("handlers.qq.accessOwner", {
3923
+ owner: redactQQOpenId(ownerOpenId)
3924
+ });
3925
+ }
3926
+ if (allowlist.length > 0) {
3927
+ return t("handlers.qq.accessAllowlist", { count: allowlist.length });
3928
+ }
3929
+ if (runtimeBoundOpenId) {
3930
+ return t("handlers.qq.accessRuntime", {
3931
+ owner: redactQQOpenId(runtimeBoundOpenId)
3932
+ });
3933
+ }
3934
+ return t("handlers.qq.accessOpen");
3935
+ }
3936
+ function formatQQSetupPrompt(step) {
3937
+ return t(step === "appId" ? "handlers.qq.promptAppId" : "handlers.qq.promptAppSecret");
3938
+ }
3939
+ function formatQQSetupWaiting(step) {
3940
+ return t(
3941
+ step === "appId" ? "handlers.qq.setupWaitingAppId" : "handlers.qq.setupWaitingAppSecret"
3942
+ );
3943
+ }
3944
+
3945
+ // src/qq/channel.ts
3946
+ var QQ_LOCK_FILE = join(homedir(), ".reasonix", "qq-channel.pid");
3947
+ var QQ_MAX_CHUNK_BYTES = 1500;
3948
+ var NATURAL_SPLIT_MIN_FRACTION = 0.6;
3949
+ function fitUtf8Slice(text, maxBytes) {
3950
+ let end = 0;
3951
+ let bytes = 0;
3952
+ for (const char of text) {
3953
+ const nextBytes = Buffer.byteLength(char, "utf8");
3954
+ if (bytes > 0 && bytes + nextBytes > maxBytes) break;
3955
+ end += char.length;
3956
+ bytes += nextBytes;
3957
+ }
3958
+ return end > 0 ? text.slice(0, end) : text.slice(0, 1);
3959
+ }
3960
+ function pickNaturalSplit(candidate) {
3961
+ const minSplit = Math.floor(candidate.length * NATURAL_SPLIT_MIN_FRACTION);
3962
+ const splitters = ["\n\n", "\n", " "];
3963
+ for (const splitter of splitters) {
3964
+ const at = candidate.lastIndexOf(splitter);
3965
+ if (at >= minSplit) return at + splitter.length;
3966
+ }
3967
+ return candidate.length;
3968
+ }
3969
+ function splitQQMessage(text, maxBytes = QQ_MAX_CHUNK_BYTES) {
3970
+ const chunks = [];
3971
+ let remaining = text;
3972
+ while (remaining.length > 0) {
3973
+ if (Buffer.byteLength(remaining, "utf8") <= maxBytes) {
3974
+ chunks.push(remaining);
3975
+ break;
3976
+ }
3977
+ const candidate = fitUtf8Slice(remaining, maxBytes);
3978
+ const splitAt = pickNaturalSplit(candidate);
3979
+ chunks.push(candidate.slice(0, splitAt));
3980
+ remaining = remaining.slice(splitAt).trimStart();
3981
+ }
3982
+ return chunks;
3983
+ }
3984
+ var QQChannel = class {
3985
+ constructor(callbacks) {
3986
+ this.callbacks = callbacks;
3987
+ }
3988
+ callbacks;
3989
+ bot = null;
3990
+ qqUserId = null;
3991
+ qqMessageId = null;
3992
+ ownerOpenId;
3993
+ allowlist;
3994
+ runtimeBoundOpenId = null;
3995
+ processedMsgIds = /* @__PURE__ */ new Set();
3996
+ processedMsgIdQueue = [];
3997
+ lockAcquired = false;
3998
+ nextOutboundMsgSeq = 1;
3999
+ rememberMessage(id) {
4000
+ if (this.processedMsgIds.has(id)) return false;
4001
+ this.processedMsgIds.add(id);
4002
+ this.processedMsgIdQueue.push(id);
4003
+ if (this.processedMsgIdQueue.length > 200) {
4004
+ const oldest = this.processedMsgIdQueue.shift();
4005
+ if (oldest) this.processedMsgIds.delete(oldest);
4006
+ }
4007
+ return true;
4008
+ }
4009
+ acquireLock() {
4010
+ try {
4011
+ const existing = Number(readFileSync(QQ_LOCK_FILE, "utf8").trim());
4012
+ if (Number.isInteger(existing) && existing > 0 && existing !== process.pid) {
4013
+ try {
4014
+ process.kill(existing, 0);
4015
+ throw new Error(t("handlers.qq.lockAlreadyRunning", { pid: existing }));
4016
+ } catch (err) {
4017
+ const e = err;
4018
+ if (e.code !== "ESRCH") throw err;
4019
+ }
4020
+ }
4021
+ } catch (err) {
4022
+ const e = err;
4023
+ if (e.code !== "ENOENT") throw err;
4024
+ }
4025
+ mkdirSync(dirname(QQ_LOCK_FILE), { recursive: true });
4026
+ writeFileSync(QQ_LOCK_FILE, String(process.pid), "utf8");
4027
+ this.lockAcquired = true;
4028
+ }
4029
+ releaseLock() {
4030
+ if (!this.lockAcquired) return;
4031
+ try {
4032
+ const existing = Number(readFileSync(QQ_LOCK_FILE, "utf8").trim());
4033
+ if (existing === process.pid) unlinkSync(QQ_LOCK_FILE);
4034
+ } catch {
4035
+ }
4036
+ this.lockAcquired = false;
4037
+ }
4038
+ applyAccessConfig(config) {
4039
+ this.ownerOpenId = config.ownerOpenId;
4040
+ this.allowlist = config.allowlist;
4041
+ if (this.ownerOpenId || (this.allowlist?.length ?? 0) > 0) {
4042
+ this.runtimeBoundOpenId = null;
4043
+ }
4044
+ }
4045
+ handlePrivateMessage(msg) {
4046
+ const text = msg.content?.trim();
4047
+ if (!text) return;
4048
+ if (!this.rememberMessage(msg.id)) return;
4049
+ const openid = msg.author.user_openid;
4050
+ const verdict = decideQQAccess(
4051
+ {
4052
+ ownerOpenId: this.ownerOpenId,
4053
+ allowlist: this.allowlist,
4054
+ runtimeBoundOpenId: this.runtimeBoundOpenId
4055
+ },
4056
+ openid
4057
+ );
4058
+ if (!verdict.accept) {
4059
+ this.callbacks.onError?.(
4060
+ t("handlers.qq.unauthorizedMessage", {
4061
+ openid: redactQQOpenId(openid),
4062
+ access: formatQQAccessSummary({
4063
+ ownerOpenId: this.ownerOpenId,
4064
+ allowlist: this.allowlist,
4065
+ runtimeBoundOpenId: this.runtimeBoundOpenId
4066
+ })
4067
+ })
4068
+ );
4069
+ return;
4070
+ }
4071
+ if (verdict.bindRuntime) {
4072
+ this.runtimeBoundOpenId = openid;
4073
+ this.callbacks.onError?.(
4074
+ t("handlers.qq.runtimeBound", {
4075
+ openid: redactQQOpenId(openid)
4076
+ })
4077
+ );
4078
+ }
4079
+ this.qqUserId = openid;
4080
+ this.qqMessageId = msg.id;
4081
+ this.callbacks.onSubmitMessage(`[QQ] ${text}`);
4082
+ }
4083
+ refreshAccessConfig() {
4084
+ this.applyAccessConfig(loadQQConfig());
4085
+ }
4086
+ describeAccess() {
4087
+ return describeQQAccess({
4088
+ ownerOpenId: this.ownerOpenId,
4089
+ allowlist: this.allowlist,
4090
+ runtimeBoundOpenId: this.runtimeBoundOpenId
4091
+ });
4092
+ }
4093
+ getRuntimeBoundOpenId() {
4094
+ return this.runtimeBoundOpenId;
4095
+ }
4096
+ async start() {
4097
+ loadDotenv();
4098
+ this.acquireLock();
4099
+ const config = loadQQConfig();
4100
+ if (!config.appId) {
4101
+ this.releaseLock();
4102
+ throw new Error(t("handlers.qq.missingAppId"));
4103
+ }
4104
+ if (!config.appSecret) {
4105
+ this.releaseLock();
4106
+ throw new Error(t("handlers.qq.missingAppSecret"));
4107
+ }
4108
+ this.applyAccessConfig(config);
4109
+ const bot = new QQBot({
4110
+ appid: config.appId,
4111
+ secret: config.appSecret,
4112
+ sandbox: config.sandbox ?? false
4113
+ });
4114
+ bot.on("online", () => {
4115
+ process.stderr.write("QQ bot is online!\n");
4116
+ });
4117
+ bot.on("bot_error", (msg) => {
4118
+ this.callbacks.onError?.(msg);
4119
+ });
4120
+ bot.on("message.private", (msg) => {
4121
+ this.handlePrivateMessage(msg);
4122
+ });
4123
+ this.bot = bot;
4124
+ try {
4125
+ await bot.start();
4126
+ const readyOrError = await Promise.race([
4127
+ new Promise((resolve) => bot.once("online", () => resolve("ready"))),
4128
+ new Promise((resolve) => bot.once("bot_error", () => resolve("error"))),
4129
+ new Promise((resolve) => setTimeout(() => resolve("timeout"), 15e3))
4130
+ ]);
4131
+ if (readyOrError === "error") {
4132
+ throw new Error(t("handlers.qq.authFailed"));
4133
+ }
4134
+ if (readyOrError === "timeout") {
4135
+ throw new Error(t("handlers.qq.readyTimeout"));
4136
+ }
4137
+ } catch (err) {
4138
+ this.releaseLock();
4139
+ throw err;
4140
+ }
4141
+ }
4142
+ async sendResponse(text) {
4143
+ if (!this.bot || !this.qqUserId) return;
4144
+ const chunks = splitQQMessage(text);
4145
+ for (let index = 0; index < chunks.length; index++) {
4146
+ const chunk = chunks[index];
4147
+ if (!chunk) continue;
4148
+ try {
4149
+ await this.bot.sendPrivateMessage(
4150
+ this.qqUserId,
4151
+ chunk,
4152
+ this.qqMessageId ?? void 0,
4153
+ this.nextOutboundMsgSeq++
4154
+ );
4155
+ } catch (err) {
4156
+ const msg = `QQ sendResponse chunk ${index + 1}/${chunks.length} failed: ${err.message}`;
4157
+ this.callbacks.onError?.(msg);
4158
+ break;
4159
+ }
4160
+ }
4161
+ }
4162
+ async stop() {
4163
+ await this.bot?.stop();
4164
+ this.releaseLock();
4165
+ }
4166
+ };
4167
+
4168
+ // src/mcp/summary.ts
4169
+ function buildMcpServerSummary(opts) {
4170
+ return {
4171
+ label: opts.label,
4172
+ spec: opts.spec,
4173
+ toolCount: opts.toolCount,
4174
+ report: opts.report,
4175
+ host: opts.host,
4176
+ bridgeEnv: opts.bridgeEnv,
4177
+ readResource(uri) {
4178
+ return opts.host.client.readResource(uri);
4179
+ },
4180
+ getPrompt(name, args) {
4181
+ return args !== void 0 ? opts.host.client.getPrompt(name, args) : opts.host.client.getPrompt(name);
4182
+ }
4183
+ };
4184
+ }
4185
+
4186
+ // src/cli/commands/mcp-runtime.ts
4187
+ var stderrLifecycleSink = (n) => {
4188
+ if (n.kind === "slow") {
4189
+ process.stderr.write(
4190
+ `${formatMcpSlowToast({ name: n.serverName, p95Ms: n.p95Ms, sampleSize: n.sampleSize })}
4191
+ `
4192
+ );
4193
+ return;
4194
+ }
4195
+ if (n.kind === "failed") {
4196
+ process.stderr.write(
4197
+ `${formatMcpLifecycleEvent({ state: "failed", name: n.name, reason: n.reason })}
4198
+ \u2192 ${t("mcpLifecycle.failedSetupHint")}
4199
+ `
4200
+ );
4201
+ return;
4202
+ }
4203
+ if (n.kind === "connected") {
4204
+ process.stderr.write(
4205
+ `${formatMcpLifecycleEvent({
4206
+ state: "connected",
4207
+ name: n.name,
4208
+ tools: n.tools,
4209
+ resources: n.resources,
4210
+ prompts: n.prompts,
4211
+ ms: n.ms
4212
+ })}
4213
+ `
4214
+ );
4215
+ return;
4216
+ }
4217
+ if (n.kind === "tools-ready") {
4218
+ process.stderr.write(
4219
+ `${formatMcpLifecycleEvent({ state: "tools-ready", name: n.name, tools: n.tools, ms: n.ms })}
4220
+ `
4221
+ );
4222
+ return;
4223
+ }
4224
+ if (n.kind === "warn") {
4225
+ process.stderr.write(
4226
+ `${formatMcpLifecycleEvent({ state: "warn", name: n.name, reason: n.reason })}
4227
+ `
4228
+ );
4229
+ return;
4230
+ }
4231
+ process.stderr.write(
4232
+ `${formatMcpLifecycleEvent({ state: n.kind, name: n.name })}
4233
+ `
4234
+ );
4235
+ };
4236
+ function createMcpRuntime(ctx) {
4237
+ const records = /* @__PURE__ */ new Map();
4238
+ const insertionOrder = [];
4239
+ const failureMap = /* @__PURE__ */ new Map();
4240
+ let sink = stderrLifecycleSink;
4241
+ async function addSpec(raw, loop, signal) {
4242
+ if (records.has(raw)) {
4243
+ return { ok: true, summary: records.get(raw).summary };
4244
+ }
4245
+ failureMap.delete(raw);
4246
+ const tools = ctx.getTools();
4247
+ if (!tools) return { ok: false, reason: "no tool registry available" };
4248
+ const cfg = readConfig();
4249
+ const normalized = normalizeMcpConfig(cfg);
4250
+ let label = "anon";
4251
+ let mcp;
4252
+ let resolveReady;
4253
+ let rejectReady;
4254
+ const ready = new Promise((resolve, reject) => {
4255
+ resolveReady = resolve;
4256
+ rejectReady = reject;
4257
+ });
4258
+ ready.catch(() => void 0);
4259
+ try {
4260
+ const parsed = parseMcpSpec(raw);
4261
+ label = parsed.name ?? "anon";
4262
+ const matched = parsed.name ? normalized.find((s) => s.name === parsed.name) : void 0;
4263
+ const spec = overlayMatchedSpec(parsed, matched);
4264
+ if (spec.disabled) {
4265
+ sink({ kind: "disabled", name: label });
4266
+ rejectReady(new Error(`MCP server "${label}" is disabled`));
4267
+ failureMap.set(raw, { spec: raw, name: label, reason: "disabled by user", at: Date.now() });
4268
+ return { ok: false, reason: "disabled by user" };
4269
+ }
4270
+ sink({ kind: "handshake", name: label });
4271
+ const t0 = Date.now();
4272
+ const namePrefix = spec.name ? `${spec.name}_` : ctx.getRequestedCount() === 1 && ctx.getMcpPrefix() ? ctx.getMcpPrefix() : "";
4273
+ if (spec.transport === "stdio") preflightStdioSpec(spec);
4274
+ const workspaceDir = ctx.getWorkspaceDir?.();
4275
+ const transport = buildTransportFromSpec(spec, { cwd: workspaceDir });
4276
+ mcp = new McpClient({ transport, workspaceDir });
4277
+ await mcp.initialize({ signal });
4278
+ const host = { client: mcp };
4279
+ const bridge = await bridgeMcpTools(mcp, {
4280
+ registry: tools,
4281
+ namePrefix,
4282
+ serverName: label,
4283
+ host,
4284
+ ready,
4285
+ onProgress: (info) => ctx.progressSink.current?.(info),
4286
+ onSlow: (info) => sink({
4287
+ kind: "slow",
4288
+ serverName: info.serverName,
4289
+ p95Ms: info.p95Ms,
4290
+ sampleSize: info.sampleSize
4291
+ })
4292
+ });
4293
+ const ms = Date.now() - t0;
4294
+ const allSpecs = tools.specs();
4295
+ const registeredSpecs = allSpecs.filter(
4296
+ (s) => bridge.registeredNames.includes(s.function.name)
4297
+ );
4298
+ records.set(raw, {
4299
+ spec: raw,
4300
+ client: mcp,
4301
+ summary: buildMcpServerSummary({
4302
+ label,
4303
+ spec: raw,
4304
+ toolCount: bridge.registeredNames.length,
4305
+ report: {
4306
+ protocolVersion: mcp.protocolVersion,
4307
+ serverInfo: mcp.serverInfo,
4308
+ capabilities: mcp.serverCapabilities ?? {},
4309
+ tools: { supported: true, items: [] },
4310
+ resources: { supported: false, reason: "still inspecting" },
4311
+ prompts: { supported: false, reason: "still inspecting" },
4312
+ elapsedMs: ms
4313
+ },
4314
+ host,
4315
+ bridgeEnv: bridge.env
4316
+ }),
4317
+ registeredNames: bridge.registeredNames,
4318
+ registeredSpecs
4319
+ });
4320
+ insertionOrder.push(raw);
4321
+ resolveReady();
4322
+ sink({
4323
+ kind: "tools-ready",
4324
+ name: label,
4325
+ tools: bridge.registeredNames.length,
4326
+ ms
4327
+ });
4328
+ let report;
4329
+ try {
4330
+ report = await inspectMcpServer(mcp);
4331
+ } catch {
4332
+ report = {
4333
+ protocolVersion: mcp.protocolVersion,
4334
+ serverInfo: mcp.serverInfo,
4335
+ capabilities: mcp.serverCapabilities ?? {},
4336
+ tools: { supported: true, items: [] },
4337
+ resources: { supported: false, reason: "inspect failed" },
4338
+ prompts: { supported: false, reason: "inspect failed" },
4339
+ elapsedMs: 0
4340
+ };
4341
+ }
4342
+ const resourceCount = report.resources.supported ? report.resources.items.length : 0;
4343
+ const promptCount = report.prompts.supported ? report.prompts.items.length : 0;
4344
+ sink({
4345
+ kind: "connected",
4346
+ name: label,
4347
+ tools: bridge.registeredNames.length,
4348
+ resources: resourceCount,
4349
+ prompts: promptCount,
4350
+ ms
4351
+ });
4352
+ const summary = buildMcpServerSummary({
4353
+ label,
4354
+ spec: raw,
4355
+ toolCount: bridge.registeredNames.length,
4356
+ report,
4357
+ host,
4358
+ bridgeEnv: bridge.env
4359
+ });
4360
+ records.set(raw, {
4361
+ spec: raw,
4362
+ client: mcp,
4363
+ summary,
4364
+ registeredNames: bridge.registeredNames,
4365
+ registeredSpecs
4366
+ });
4367
+ if (loop)
4368
+ for (const s of registeredSpecs)
4369
+ try {
4370
+ loop.prefix.addTool(s);
4371
+ } catch (err) {
4372
+ sink({
4373
+ kind: "warn",
4374
+ name: label,
4375
+ reason: `addTool failed for ${s.function.name}: ${err.message}`
4376
+ });
4377
+ }
4378
+ return { ok: true, summary };
4379
+ } catch (err) {
4380
+ const reason = err.message;
4381
+ if (!records.has(raw)) {
4382
+ await mcp?.close().catch(() => void 0);
4383
+ rejectReady(new Error(`MCP server "${label}" failed to start: ${reason}`));
4384
+ sink({ kind: "failed", name: label, reason });
4385
+ failureMap.set(raw, { spec: raw, name: label, reason, at: Date.now() });
4386
+ return { ok: false, reason };
4387
+ }
4388
+ sink({ kind: "warn", name: label, reason });
4389
+ return { ok: true, summary: records.get(raw).summary };
4390
+ }
4391
+ }
4392
+ async function removeSpec(raw, loop) {
4393
+ failureMap.delete(raw);
4394
+ const record = records.get(raw);
4395
+ if (!record) return false;
4396
+ await record.client.close().catch(() => void 0);
4397
+ const tools = ctx.getTools();
4398
+ for (const name of record.registeredNames) {
4399
+ tools?.unregister(name);
4400
+ loop?.prefix.removeTool(name);
4401
+ }
4402
+ records.delete(raw);
4403
+ const idx = insertionOrder.indexOf(raw);
4404
+ if (idx >= 0) insertionOrder.splice(idx, 1);
4405
+ return true;
4406
+ }
4407
+ async function reloadFromConfig(loop) {
4408
+ const normalized = normalizeMcpConfig(readConfig());
4409
+ const desired = normalized.map(specToRaw);
4410
+ const desiredSet = new Set(desired);
4411
+ const currentSet = new Set(records.keys());
4412
+ const added = [];
4413
+ const removed = [];
4414
+ const failed = [];
4415
+ for (const spec of [...currentSet]) {
4416
+ if (!desiredSet.has(spec)) {
4417
+ await removeSpec(spec, loop);
4418
+ removed.push(spec);
4419
+ }
4420
+ }
4421
+ for (const spec of desired) {
4422
+ if (currentSet.has(spec)) continue;
4423
+ const result = await addSpec(spec, loop);
4424
+ if (result.ok) added.push(spec);
4425
+ else failed.push({ spec, reason: result.reason });
4426
+ }
4427
+ return { added, removed, failed, summaries: summaries() };
4428
+ }
4429
+ function specs() {
4430
+ return [...insertionOrder];
4431
+ }
4432
+ function summaries() {
4433
+ return insertionOrder.map((s) => records.get(s)?.summary).filter((s) => Boolean(s));
4434
+ }
4435
+ async function closeAll() {
4436
+ for (const r of records.values()) await r.client.close().catch(() => void 0);
4437
+ records.clear();
4438
+ insertionOrder.length = 0;
4439
+ failureMap.clear();
4440
+ }
4441
+ function failures() {
4442
+ return [...failureMap.values()];
4443
+ }
4444
+ function setLifecycleSink(s) {
4445
+ sink = s;
4446
+ }
4447
+ return {
4448
+ size: () => records.size,
4449
+ specs,
4450
+ summaries,
4451
+ failures,
4452
+ addSpec,
4453
+ removeSpec,
4454
+ reloadFromConfig,
4455
+ closeAll,
4456
+ setLifecycleSink
4457
+ };
4458
+ }
4459
+
3641
4460
  export {
3642
- wrapper_default
4461
+ formatQQModeLabel,
4462
+ formatQQAccessSummary,
4463
+ formatQQSetupPrompt,
4464
+ formatQQSetupWaiting,
4465
+ QQChannel,
4466
+ createMcpRuntime
3643
4467
  };
3644
- //# sourceMappingURL=chunk-FEZK652I.js.map
4468
+ //# sourceMappingURL=chunk-MVLPXZAA.js.map