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,832 +0,0 @@
1
- #!/usr/bin/env node
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-5AIDYVH2.js";
7
- import {
8
- buildTransportFromSpec,
9
- preflightStdioSpec
10
- } from "./chunk-SVD4UPRQ.js";
11
- import {
12
- bridgeMcpTools
13
- } from "./chunk-K4YQFULP.js";
14
- import {
15
- McpClient,
16
- inspectMcpServer
17
- } from "./chunk-7YPMTE3U.js";
18
- import {
19
- wrapper_default
20
- } from "./chunk-FEZK652I.js";
21
- import {
22
- loadDotenv
23
- } from "./chunk-2UQP6H6T.js";
24
- import {
25
- t
26
- } from "./chunk-DVD67FXQ.js";
27
- import {
28
- decideQQAccess,
29
- describeQQAccess,
30
- loadQQConfig,
31
- normalizeMcpConfig,
32
- normalizeQQAllowlist,
33
- normalizeQQOpenId,
34
- overlayMatchedSpec,
35
- parseMcpSpec,
36
- readConfig,
37
- redactQQOpenId,
38
- specToRaw
39
- } from "./chunk-A5PBEIJ7.js";
40
-
41
- // src/qq/channel.ts
42
- import { mkdirSync, readFileSync, unlinkSync, writeFileSync } from "fs";
43
- import { homedir } from "os";
44
- import { dirname, join } from "path";
45
-
46
- // src/qq/bot.ts
47
- import { EventEmitter } from "events";
48
- var TOKEN_URL = "https://bots.qq.com/app/getAppAccessToken";
49
- var BASE_URL = "https://api.sgroup.qq.com";
50
- var SANDBOX_URL = "https://sandbox.api.sgroup.qq.com";
51
- var INTENT_C2C_GROUP = 1 << 25;
52
- var MIN_HEARTBEAT_INTERVAL_MS = 5e3;
53
- var MAX_HEARTBEAT_INTERVAL_MS = 6e4;
54
- var ALLOWED_GATEWAY_HOSTS = ["api.sgroup.qq.com", "sandbox.api.sgroup.qq.com", "qq.com"];
55
- var QQBot = class extends EventEmitter {
56
- config;
57
- token = "";
58
- tokenExpiresAt = 0;
59
- ws = null;
60
- heartbeatTimer = null;
61
- seq = 0;
62
- sessionId = "";
63
- closed = false;
64
- readyReceived = false;
65
- constructor(config) {
66
- super();
67
- this.config = config;
68
- }
69
- get baseUrl() {
70
- return this.config.sandbox ? SANDBOX_URL : BASE_URL;
71
- }
72
- sanitizeHeartbeatInterval(interval) {
73
- if (typeof interval !== "number" || !Number.isFinite(interval)) {
74
- return null;
75
- }
76
- if (interval < MIN_HEARTBEAT_INTERVAL_MS) {
77
- return MIN_HEARTBEAT_INTERVAL_MS;
78
- }
79
- if (interval > MAX_HEARTBEAT_INTERVAL_MS) {
80
- return MAX_HEARTBEAT_INTERVAL_MS;
81
- }
82
- return Math.trunc(interval);
83
- }
84
- validateGatewayUrl(rawUrl) {
85
- const url = new URL(rawUrl);
86
- const trustedHost = ALLOWED_GATEWAY_HOSTS.some(
87
- (host) => url.hostname === host || url.hostname.endsWith(`.${host}`)
88
- );
89
- if (url.protocol !== "wss:" || !trustedHost || url.username || url.password || url.search || url.hash) {
90
- throw new Error(`Unexpected QQ gateway URL: ${rawUrl}`);
91
- }
92
- return url.toString();
93
- }
94
- async ensureToken() {
95
- if (this.token && Date.now() < this.tokenExpiresAt - 6e4) {
96
- return this.token;
97
- }
98
- const res = await fetch(TOKEN_URL, {
99
- method: "POST",
100
- headers: { "Content-Type": "application/json" },
101
- body: JSON.stringify({
102
- appId: this.config.appid,
103
- clientSecret: this.config.secret
104
- })
105
- });
106
- if (!res.ok) {
107
- const text = await res.text();
108
- throw new Error(`Failed to get access token (${res.status}): ${text}`);
109
- }
110
- const data = await res.json();
111
- this.token = data.access_token;
112
- this.tokenExpiresAt = Date.now() + data.expires_in * 1e3;
113
- return this.token;
114
- }
115
- async getGateway() {
116
- const token = await this.ensureToken();
117
- const res = await fetch(`${this.baseUrl}/gateway`, {
118
- headers: { Authorization: `QQBot ${token}` }
119
- });
120
- if (!res.ok) {
121
- const text = await res.text();
122
- throw new Error(`Failed to get gateway (${res.status}): ${text}`);
123
- }
124
- const data = await res.json();
125
- return this.validateGatewayUrl(data.url);
126
- }
127
- sendOp(op, data) {
128
- if (!this.ws) return;
129
- this.ws.send(JSON.stringify({ op, d: data ?? {} }));
130
- }
131
- async handlePayload(payload) {
132
- switch (payload.op) {
133
- case 10: {
134
- const d = payload.d;
135
- this.sendOp(2, {
136
- token: `QQBot ${await this.ensureToken()}`,
137
- intents: INTENT_C2C_GROUP,
138
- shard: [0, 1]
139
- });
140
- const heartbeatInterval = this.sanitizeHeartbeatInterval(d?.heartbeat_interval);
141
- if (heartbeatInterval) {
142
- this.heartbeatTimer = setInterval(() => {
143
- this.sendOp(1, this.seq || null);
144
- }, heartbeatInterval);
145
- }
146
- break;
147
- }
148
- case 0: {
149
- if (payload.s) this.seq = payload.s;
150
- if (payload.t === "READY") {
151
- const d = payload.d;
152
- this.sessionId = d.session_id;
153
- this.readyReceived = true;
154
- this.emit("online");
155
- } else if (payload.t === "C2C_MESSAGE_CREATE") {
156
- this.emit("message.private", payload.d);
157
- } else if (payload.t === "GROUP_AT_MESSAGE_CREATE") {
158
- this.emit("message.group", payload.d);
159
- }
160
- break;
161
- }
162
- case 7: {
163
- this.reconnect();
164
- break;
165
- }
166
- case 9: {
167
- this.sessionId = "";
168
- this.sendOp(2, {
169
- token: `QQBot ${await this.ensureToken()}`,
170
- intents: INTENT_C2C_GROUP,
171
- shard: [0, 1]
172
- });
173
- break;
174
- }
175
- }
176
- }
177
- async reconnect() {
178
- this.cleanup();
179
- await this.connect();
180
- }
181
- cleanup() {
182
- if (this.heartbeatTimer) {
183
- clearInterval(this.heartbeatTimer);
184
- this.heartbeatTimer = null;
185
- }
186
- if (this.ws) {
187
- this.ws.removeAllListeners();
188
- this.ws.close();
189
- this.ws = null;
190
- }
191
- }
192
- async connect() {
193
- const gatewayUrl = await this.getGateway();
194
- const token = await this.ensureToken();
195
- this.ws = new wrapper_default(gatewayUrl, {
196
- headers: {
197
- Authorization: `QQBot ${token}`,
198
- "X-Union-Appid": this.config.appid
199
- }
200
- });
201
- this.ws.on("open", () => {
202
- if (this.sessionId) {
203
- this.sendOp(6, {
204
- token: `QQBot ${this.token}`,
205
- session_id: this.sessionId,
206
- seq: this.seq
207
- });
208
- }
209
- });
210
- this.ws.on("message", (raw) => {
211
- try {
212
- const payload = JSON.parse(raw.toString());
213
- this.handlePayload(payload).catch(() => {
214
- });
215
- } catch {
216
- }
217
- });
218
- this.ws.on("close", () => {
219
- if (!this.closed) {
220
- if (this.readyReceived) {
221
- console.error("QQ WebSocket reconnecting...");
222
- this.cleanup();
223
- setTimeout(() => this.reconnect(), 3e3);
224
- } else {
225
- const msg = "QQ WebSocket closed before authentication completed \u2014 check your appId and appSecret";
226
- this.emit("bot_error", msg);
227
- this.closed = true;
228
- }
229
- }
230
- });
231
- this.ws.on("error", (err) => {
232
- const msg = `QQ WebSocket error: ${err.message}`;
233
- console.error(msg);
234
- this.emit("bot_error", msg);
235
- });
236
- }
237
- async start() {
238
- this.closed = false;
239
- this.readyReceived = false;
240
- await this.connect();
241
- }
242
- async stop() {
243
- this.closed = true;
244
- this.cleanup();
245
- }
246
- async sendPrivateMessage(openid, content, msgId, msgSeq) {
247
- const token = await this.ensureToken();
248
- const body = {
249
- content,
250
- msg_type: 0
251
- };
252
- if (msgId) body.msg_id = msgId;
253
- if (typeof msgSeq === "number" && Number.isFinite(msgSeq)) body.msg_seq = Math.trunc(msgSeq);
254
- const res = await fetch(`${this.baseUrl}/v2/users/${encodeURIComponent(openid)}/messages`, {
255
- method: "POST",
256
- headers: {
257
- Authorization: `QQBot ${token}`,
258
- "Content-Type": "application/json",
259
- "X-Union-Appid": this.config.appid
260
- },
261
- body: JSON.stringify(body)
262
- });
263
- if (!res.ok) {
264
- const text = await res.text();
265
- const msg = `QQ sendPrivateMessage failed (${res.status}): ${text}`;
266
- throw new Error(msg);
267
- }
268
- }
269
- };
270
-
271
- // src/qq/strings.ts
272
- function formatQQModeLabel(codeMode) {
273
- return t(codeMode ? "handlers.qq.modeCode" : "handlers.qq.modeChat");
274
- }
275
- function formatQQAccessSummary(config) {
276
- const ownerOpenId = normalizeQQOpenId(config.ownerOpenId);
277
- const allowlist = normalizeQQAllowlist(config.allowlist) ?? [];
278
- const runtimeBoundOpenId = normalizeQQOpenId(config.runtimeBoundOpenId);
279
- if (ownerOpenId) {
280
- if (allowlist.length > 0) {
281
- return t("handlers.qq.accessOwnerWithAllowlist", {
282
- owner: redactQQOpenId(ownerOpenId),
283
- count: allowlist.length
284
- });
285
- }
286
- return t("handlers.qq.accessOwner", {
287
- owner: redactQQOpenId(ownerOpenId)
288
- });
289
- }
290
- if (allowlist.length > 0) {
291
- return t("handlers.qq.accessAllowlist", { count: allowlist.length });
292
- }
293
- if (runtimeBoundOpenId) {
294
- return t("handlers.qq.accessRuntime", {
295
- owner: redactQQOpenId(runtimeBoundOpenId)
296
- });
297
- }
298
- return t("handlers.qq.accessOpen");
299
- }
300
- function formatQQSetupPrompt(step) {
301
- return t(step === "appId" ? "handlers.qq.promptAppId" : "handlers.qq.promptAppSecret");
302
- }
303
- function formatQQSetupWaiting(step) {
304
- return t(
305
- step === "appId" ? "handlers.qq.setupWaitingAppId" : "handlers.qq.setupWaitingAppSecret"
306
- );
307
- }
308
-
309
- // src/qq/channel.ts
310
- var QQ_LOCK_FILE = join(homedir(), ".reasonix", "qq-channel.pid");
311
- var QQ_MAX_CHUNK_BYTES = 1500;
312
- var NATURAL_SPLIT_MIN_FRACTION = 0.6;
313
- function fitUtf8Slice(text, maxBytes) {
314
- let end = 0;
315
- let bytes = 0;
316
- for (const char of text) {
317
- const nextBytes = Buffer.byteLength(char, "utf8");
318
- if (bytes > 0 && bytes + nextBytes > maxBytes) break;
319
- end += char.length;
320
- bytes += nextBytes;
321
- }
322
- return end > 0 ? text.slice(0, end) : text.slice(0, 1);
323
- }
324
- function pickNaturalSplit(candidate) {
325
- const minSplit = Math.floor(candidate.length * NATURAL_SPLIT_MIN_FRACTION);
326
- const splitters = ["\n\n", "\n", " "];
327
- for (const splitter of splitters) {
328
- const at = candidate.lastIndexOf(splitter);
329
- if (at >= minSplit) return at + splitter.length;
330
- }
331
- return candidate.length;
332
- }
333
- function splitQQMessage(text, maxBytes = QQ_MAX_CHUNK_BYTES) {
334
- const chunks = [];
335
- let remaining = text;
336
- while (remaining.length > 0) {
337
- if (Buffer.byteLength(remaining, "utf8") <= maxBytes) {
338
- chunks.push(remaining);
339
- break;
340
- }
341
- const candidate = fitUtf8Slice(remaining, maxBytes);
342
- const splitAt = pickNaturalSplit(candidate);
343
- chunks.push(candidate.slice(0, splitAt));
344
- remaining = remaining.slice(splitAt).trimStart();
345
- }
346
- return chunks;
347
- }
348
- var QQChannel = class {
349
- constructor(callbacks) {
350
- this.callbacks = callbacks;
351
- }
352
- callbacks;
353
- bot = null;
354
- qqUserId = null;
355
- qqMessageId = null;
356
- ownerOpenId;
357
- allowlist;
358
- runtimeBoundOpenId = null;
359
- processedMsgIds = /* @__PURE__ */ new Set();
360
- processedMsgIdQueue = [];
361
- lockAcquired = false;
362
- nextOutboundMsgSeq = 1;
363
- rememberMessage(id) {
364
- if (this.processedMsgIds.has(id)) return false;
365
- this.processedMsgIds.add(id);
366
- this.processedMsgIdQueue.push(id);
367
- if (this.processedMsgIdQueue.length > 200) {
368
- const oldest = this.processedMsgIdQueue.shift();
369
- if (oldest) this.processedMsgIds.delete(oldest);
370
- }
371
- return true;
372
- }
373
- acquireLock() {
374
- try {
375
- const existing = Number(readFileSync(QQ_LOCK_FILE, "utf8").trim());
376
- if (Number.isInteger(existing) && existing > 0 && existing !== process.pid) {
377
- try {
378
- process.kill(existing, 0);
379
- throw new Error(t("handlers.qq.lockAlreadyRunning", { pid: existing }));
380
- } catch (err) {
381
- const e = err;
382
- if (e.code !== "ESRCH") throw err;
383
- }
384
- }
385
- } catch (err) {
386
- const e = err;
387
- if (e.code !== "ENOENT") throw err;
388
- }
389
- mkdirSync(dirname(QQ_LOCK_FILE), { recursive: true });
390
- writeFileSync(QQ_LOCK_FILE, String(process.pid), "utf8");
391
- this.lockAcquired = true;
392
- }
393
- releaseLock() {
394
- if (!this.lockAcquired) return;
395
- try {
396
- const existing = Number(readFileSync(QQ_LOCK_FILE, "utf8").trim());
397
- if (existing === process.pid) unlinkSync(QQ_LOCK_FILE);
398
- } catch {
399
- }
400
- this.lockAcquired = false;
401
- }
402
- applyAccessConfig(config) {
403
- this.ownerOpenId = config.ownerOpenId;
404
- this.allowlist = config.allowlist;
405
- if (this.ownerOpenId || (this.allowlist?.length ?? 0) > 0) {
406
- this.runtimeBoundOpenId = null;
407
- }
408
- }
409
- handlePrivateMessage(msg) {
410
- const text = msg.content?.trim();
411
- if (!text) return;
412
- if (!this.rememberMessage(msg.id)) return;
413
- const openid = msg.author.user_openid;
414
- const verdict = decideQQAccess(
415
- {
416
- ownerOpenId: this.ownerOpenId,
417
- allowlist: this.allowlist,
418
- runtimeBoundOpenId: this.runtimeBoundOpenId
419
- },
420
- openid
421
- );
422
- if (!verdict.accept) {
423
- this.callbacks.onError?.(
424
- t("handlers.qq.unauthorizedMessage", {
425
- openid: redactQQOpenId(openid),
426
- access: formatQQAccessSummary({
427
- ownerOpenId: this.ownerOpenId,
428
- allowlist: this.allowlist,
429
- runtimeBoundOpenId: this.runtimeBoundOpenId
430
- })
431
- })
432
- );
433
- return;
434
- }
435
- if (verdict.bindRuntime) {
436
- this.runtimeBoundOpenId = openid;
437
- this.callbacks.onError?.(
438
- t("handlers.qq.runtimeBound", {
439
- openid: redactQQOpenId(openid)
440
- })
441
- );
442
- }
443
- this.qqUserId = openid;
444
- this.qqMessageId = msg.id;
445
- this.callbacks.onSubmitMessage(`[QQ] ${text}`);
446
- }
447
- refreshAccessConfig() {
448
- this.applyAccessConfig(loadQQConfig());
449
- }
450
- describeAccess() {
451
- return describeQQAccess({
452
- ownerOpenId: this.ownerOpenId,
453
- allowlist: this.allowlist,
454
- runtimeBoundOpenId: this.runtimeBoundOpenId
455
- });
456
- }
457
- getRuntimeBoundOpenId() {
458
- return this.runtimeBoundOpenId;
459
- }
460
- async start() {
461
- loadDotenv();
462
- this.acquireLock();
463
- const config = loadQQConfig();
464
- if (!config.appId) {
465
- this.releaseLock();
466
- throw new Error(t("handlers.qq.missingAppId"));
467
- }
468
- if (!config.appSecret) {
469
- this.releaseLock();
470
- throw new Error(t("handlers.qq.missingAppSecret"));
471
- }
472
- this.applyAccessConfig(config);
473
- const bot = new QQBot({
474
- appid: config.appId,
475
- secret: config.appSecret,
476
- sandbox: config.sandbox ?? false
477
- });
478
- bot.on("online", () => {
479
- process.stderr.write("QQ bot is online!\n");
480
- });
481
- bot.on("bot_error", (msg) => {
482
- this.callbacks.onError?.(msg);
483
- });
484
- bot.on("message.private", (msg) => {
485
- this.handlePrivateMessage(msg);
486
- });
487
- this.bot = bot;
488
- try {
489
- await bot.start();
490
- const readyOrError = await Promise.race([
491
- new Promise((resolve) => bot.once("online", () => resolve("ready"))),
492
- new Promise((resolve) => bot.once("bot_error", () => resolve("error"))),
493
- new Promise((resolve) => setTimeout(() => resolve("timeout"), 15e3))
494
- ]);
495
- if (readyOrError === "error") {
496
- throw new Error(t("handlers.qq.authFailed"));
497
- }
498
- if (readyOrError === "timeout") {
499
- throw new Error(t("handlers.qq.readyTimeout"));
500
- }
501
- } catch (err) {
502
- this.releaseLock();
503
- throw err;
504
- }
505
- }
506
- async sendResponse(text) {
507
- if (!this.bot || !this.qqUserId) return;
508
- const chunks = splitQQMessage(text);
509
- for (let index = 0; index < chunks.length; index++) {
510
- const chunk = chunks[index];
511
- if (!chunk) continue;
512
- try {
513
- await this.bot.sendPrivateMessage(
514
- this.qqUserId,
515
- chunk,
516
- this.qqMessageId ?? void 0,
517
- this.nextOutboundMsgSeq++
518
- );
519
- } catch (err) {
520
- const msg = `QQ sendResponse chunk ${index + 1}/${chunks.length} failed: ${err.message}`;
521
- this.callbacks.onError?.(msg);
522
- break;
523
- }
524
- }
525
- }
526
- async stop() {
527
- await this.bot?.stop();
528
- this.releaseLock();
529
- }
530
- };
531
-
532
- // src/mcp/summary.ts
533
- function buildMcpServerSummary(opts) {
534
- return {
535
- label: opts.label,
536
- spec: opts.spec,
537
- toolCount: opts.toolCount,
538
- report: opts.report,
539
- host: opts.host,
540
- bridgeEnv: opts.bridgeEnv,
541
- readResource(uri) {
542
- return opts.host.client.readResource(uri);
543
- },
544
- getPrompt(name, args) {
545
- return args !== void 0 ? opts.host.client.getPrompt(name, args) : opts.host.client.getPrompt(name);
546
- }
547
- };
548
- }
549
-
550
- // src/cli/commands/mcp-runtime.ts
551
- var stderrLifecycleSink = (n) => {
552
- if (n.kind === "slow") {
553
- process.stderr.write(
554
- `${formatMcpSlowToast({ name: n.serverName, p95Ms: n.p95Ms, sampleSize: n.sampleSize })}
555
- `
556
- );
557
- return;
558
- }
559
- if (n.kind === "failed") {
560
- process.stderr.write(
561
- `${formatMcpLifecycleEvent({ state: "failed", name: n.name, reason: n.reason })}
562
- \u2192 ${t("mcpLifecycle.failedSetupHint")}
563
- `
564
- );
565
- return;
566
- }
567
- if (n.kind === "connected") {
568
- process.stderr.write(
569
- `${formatMcpLifecycleEvent({
570
- state: "connected",
571
- name: n.name,
572
- tools: n.tools,
573
- resources: n.resources,
574
- prompts: n.prompts,
575
- ms: n.ms
576
- })}
577
- `
578
- );
579
- return;
580
- }
581
- if (n.kind === "tools-ready") {
582
- process.stderr.write(
583
- `${formatMcpLifecycleEvent({ state: "tools-ready", name: n.name, tools: n.tools, ms: n.ms })}
584
- `
585
- );
586
- return;
587
- }
588
- if (n.kind === "warn") {
589
- process.stderr.write(
590
- `${formatMcpLifecycleEvent({ state: "warn", name: n.name, reason: n.reason })}
591
- `
592
- );
593
- return;
594
- }
595
- process.stderr.write(
596
- `${formatMcpLifecycleEvent({ state: n.kind, name: n.name })}
597
- `
598
- );
599
- };
600
- function createMcpRuntime(ctx) {
601
- const records = /* @__PURE__ */ new Map();
602
- const insertionOrder = [];
603
- const failureMap = /* @__PURE__ */ new Map();
604
- let sink = stderrLifecycleSink;
605
- async function addSpec(raw, loop, signal) {
606
- if (records.has(raw)) {
607
- return { ok: true, summary: records.get(raw).summary };
608
- }
609
- failureMap.delete(raw);
610
- const tools = ctx.getTools();
611
- if (!tools) return { ok: false, reason: "no tool registry available" };
612
- const cfg = readConfig();
613
- const normalized = normalizeMcpConfig(cfg);
614
- let label = "anon";
615
- let mcp;
616
- let resolveReady;
617
- let rejectReady;
618
- const ready = new Promise((resolve, reject) => {
619
- resolveReady = resolve;
620
- rejectReady = reject;
621
- });
622
- ready.catch(() => void 0);
623
- try {
624
- const parsed = parseMcpSpec(raw);
625
- label = parsed.name ?? "anon";
626
- const matched = parsed.name ? normalized.find((s) => s.name === parsed.name) : void 0;
627
- const spec = overlayMatchedSpec(parsed, matched);
628
- if (spec.disabled) {
629
- sink({ kind: "disabled", name: label });
630
- rejectReady(new Error(`MCP server "${label}" is disabled`));
631
- failureMap.set(raw, { spec: raw, name: label, reason: "disabled by user", at: Date.now() });
632
- return { ok: false, reason: "disabled by user" };
633
- }
634
- sink({ kind: "handshake", name: label });
635
- const t0 = Date.now();
636
- const namePrefix = spec.name ? `${spec.name}_` : ctx.getRequestedCount() === 1 && ctx.getMcpPrefix() ? ctx.getMcpPrefix() : "";
637
- if (spec.transport === "stdio") preflightStdioSpec(spec);
638
- const workspaceDir = ctx.getWorkspaceDir?.();
639
- const transport = buildTransportFromSpec(spec, { cwd: workspaceDir });
640
- mcp = new McpClient({ transport, workspaceDir });
641
- await mcp.initialize({ signal });
642
- const host = { client: mcp };
643
- const bridge = await bridgeMcpTools(mcp, {
644
- registry: tools,
645
- namePrefix,
646
- serverName: label,
647
- host,
648
- ready,
649
- onProgress: (info) => ctx.progressSink.current?.(info),
650
- onSlow: (info) => sink({
651
- kind: "slow",
652
- serverName: info.serverName,
653
- p95Ms: info.p95Ms,
654
- sampleSize: info.sampleSize
655
- })
656
- });
657
- const ms = Date.now() - t0;
658
- const allSpecs = tools.specs();
659
- const registeredSpecs = allSpecs.filter(
660
- (s) => bridge.registeredNames.includes(s.function.name)
661
- );
662
- records.set(raw, {
663
- spec: raw,
664
- client: mcp,
665
- summary: buildMcpServerSummary({
666
- label,
667
- spec: raw,
668
- toolCount: bridge.registeredNames.length,
669
- report: {
670
- protocolVersion: mcp.protocolVersion,
671
- serverInfo: mcp.serverInfo,
672
- capabilities: mcp.serverCapabilities ?? {},
673
- tools: { supported: true, items: [] },
674
- resources: { supported: false, reason: "still inspecting" },
675
- prompts: { supported: false, reason: "still inspecting" },
676
- elapsedMs: ms
677
- },
678
- host,
679
- bridgeEnv: bridge.env
680
- }),
681
- registeredNames: bridge.registeredNames,
682
- registeredSpecs
683
- });
684
- insertionOrder.push(raw);
685
- resolveReady();
686
- sink({
687
- kind: "tools-ready",
688
- name: label,
689
- tools: bridge.registeredNames.length,
690
- ms
691
- });
692
- let report;
693
- try {
694
- report = await inspectMcpServer(mcp);
695
- } catch {
696
- report = {
697
- protocolVersion: mcp.protocolVersion,
698
- serverInfo: mcp.serverInfo,
699
- capabilities: mcp.serverCapabilities ?? {},
700
- tools: { supported: true, items: [] },
701
- resources: { supported: false, reason: "inspect failed" },
702
- prompts: { supported: false, reason: "inspect failed" },
703
- elapsedMs: 0
704
- };
705
- }
706
- const resourceCount = report.resources.supported ? report.resources.items.length : 0;
707
- const promptCount = report.prompts.supported ? report.prompts.items.length : 0;
708
- sink({
709
- kind: "connected",
710
- name: label,
711
- tools: bridge.registeredNames.length,
712
- resources: resourceCount,
713
- prompts: promptCount,
714
- ms
715
- });
716
- const summary = buildMcpServerSummary({
717
- label,
718
- spec: raw,
719
- toolCount: bridge.registeredNames.length,
720
- report,
721
- host,
722
- bridgeEnv: bridge.env
723
- });
724
- records.set(raw, {
725
- spec: raw,
726
- client: mcp,
727
- summary,
728
- registeredNames: bridge.registeredNames,
729
- registeredSpecs
730
- });
731
- if (loop)
732
- for (const s of registeredSpecs)
733
- try {
734
- loop.prefix.addTool(s);
735
- } catch (err) {
736
- sink({
737
- kind: "warn",
738
- name: label,
739
- reason: `addTool failed for ${s.function.name}: ${err.message}`
740
- });
741
- }
742
- return { ok: true, summary };
743
- } catch (err) {
744
- const reason = err.message;
745
- if (!records.has(raw)) {
746
- await mcp?.close().catch(() => void 0);
747
- rejectReady(new Error(`MCP server "${label}" failed to start: ${reason}`));
748
- sink({ kind: "failed", name: label, reason });
749
- failureMap.set(raw, { spec: raw, name: label, reason, at: Date.now() });
750
- return { ok: false, reason };
751
- }
752
- sink({ kind: "warn", name: label, reason });
753
- return { ok: true, summary: records.get(raw).summary };
754
- }
755
- }
756
- async function removeSpec(raw, loop) {
757
- failureMap.delete(raw);
758
- const record = records.get(raw);
759
- if (!record) return false;
760
- await record.client.close().catch(() => void 0);
761
- const tools = ctx.getTools();
762
- for (const name of record.registeredNames) {
763
- tools?.unregister(name);
764
- loop?.prefix.removeTool(name);
765
- }
766
- records.delete(raw);
767
- const idx = insertionOrder.indexOf(raw);
768
- if (idx >= 0) insertionOrder.splice(idx, 1);
769
- return true;
770
- }
771
- async function reloadFromConfig(loop) {
772
- const normalized = normalizeMcpConfig(readConfig());
773
- const desired = normalized.map(specToRaw);
774
- const desiredSet = new Set(desired);
775
- const currentSet = new Set(records.keys());
776
- const added = [];
777
- const removed = [];
778
- const failed = [];
779
- for (const spec of [...currentSet]) {
780
- if (!desiredSet.has(spec)) {
781
- await removeSpec(spec, loop);
782
- removed.push(spec);
783
- }
784
- }
785
- for (const spec of desired) {
786
- if (currentSet.has(spec)) continue;
787
- const result = await addSpec(spec, loop);
788
- if (result.ok) added.push(spec);
789
- else failed.push({ spec, reason: result.reason });
790
- }
791
- return { added, removed, failed, summaries: summaries() };
792
- }
793
- function specs() {
794
- return [...insertionOrder];
795
- }
796
- function summaries() {
797
- return insertionOrder.map((s) => records.get(s)?.summary).filter((s) => Boolean(s));
798
- }
799
- async function closeAll() {
800
- for (const r of records.values()) await r.client.close().catch(() => void 0);
801
- records.clear();
802
- insertionOrder.length = 0;
803
- failureMap.clear();
804
- }
805
- function failures() {
806
- return [...failureMap.values()];
807
- }
808
- function setLifecycleSink(s) {
809
- sink = s;
810
- }
811
- return {
812
- size: () => records.size,
813
- specs,
814
- summaries,
815
- failures,
816
- addSpec,
817
- removeSpec,
818
- reloadFromConfig,
819
- closeAll,
820
- setLifecycleSink
821
- };
822
- }
823
-
824
- export {
825
- formatQQModeLabel,
826
- formatQQAccessSummary,
827
- formatQQSetupPrompt,
828
- formatQQSetupWaiting,
829
- QQChannel,
830
- createMcpRuntime
831
- };
832
- //# sourceMappingURL=chunk-5BBC6YMV.js.map