opc-agent 2.0.0 → 2.0.2

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 (157) hide show
  1. package/README.md +545 -365
  2. package/dist/channels/email.d.ts +32 -26
  3. package/dist/channels/email.js +239 -62
  4. package/dist/channels/feishu.d.ts +21 -6
  5. package/dist/channels/feishu.js +225 -126
  6. package/dist/channels/websocket.d.ts +46 -3
  7. package/dist/channels/websocket.js +306 -37
  8. package/dist/channels/wechat.d.ts +33 -13
  9. package/dist/channels/wechat.js +229 -42
  10. package/dist/cli.js +712 -11
  11. package/dist/core/a2a.d.ts +17 -0
  12. package/dist/core/a2a.js +43 -1
  13. package/dist/core/agent.d.ts +16 -0
  14. package/dist/core/agent.js +108 -0
  15. package/dist/core/runtime.d.ts +6 -0
  16. package/dist/core/runtime.js +161 -2
  17. package/dist/core/sandbox.d.ts +26 -0
  18. package/dist/core/sandbox.js +117 -0
  19. package/dist/core/workflow-graph.d.ts +93 -0
  20. package/dist/core/workflow-graph.js +247 -0
  21. package/dist/doctor.d.ts +15 -0
  22. package/dist/doctor.js +183 -0
  23. package/dist/eval/index.d.ts +65 -0
  24. package/dist/eval/index.js +191 -0
  25. package/dist/index.d.ts +32 -6
  26. package/dist/index.js +63 -4
  27. package/dist/plugins/content-filter.d.ts +7 -0
  28. package/dist/plugins/content-filter.js +25 -0
  29. package/dist/plugins/index.d.ts +42 -0
  30. package/dist/plugins/index.js +108 -2
  31. package/dist/plugins/logger.d.ts +6 -0
  32. package/dist/plugins/logger.js +20 -0
  33. package/dist/plugins/rate-limiter.d.ts +7 -0
  34. package/dist/plugins/rate-limiter.js +35 -0
  35. package/dist/protocols/a2a/client.d.ts +25 -0
  36. package/dist/protocols/a2a/client.js +115 -0
  37. package/dist/protocols/a2a/index.d.ts +6 -0
  38. package/dist/protocols/a2a/index.js +12 -0
  39. package/dist/protocols/a2a/server.d.ts +41 -0
  40. package/dist/protocols/a2a/server.js +295 -0
  41. package/dist/protocols/a2a/types.d.ts +91 -0
  42. package/dist/protocols/a2a/types.js +15 -0
  43. package/dist/protocols/a2a/utils.d.ts +6 -0
  44. package/dist/protocols/a2a/utils.js +47 -0
  45. package/dist/protocols/agui/client.d.ts +10 -0
  46. package/dist/protocols/agui/client.js +75 -0
  47. package/dist/protocols/agui/index.d.ts +4 -0
  48. package/dist/protocols/agui/index.js +25 -0
  49. package/dist/protocols/agui/server.d.ts +37 -0
  50. package/dist/protocols/agui/server.js +191 -0
  51. package/dist/protocols/agui/types.d.ts +107 -0
  52. package/dist/protocols/agui/types.js +17 -0
  53. package/dist/protocols/index.d.ts +2 -0
  54. package/dist/protocols/index.js +19 -0
  55. package/dist/protocols/mcp/agent-tools.d.ts +11 -0
  56. package/dist/protocols/mcp/agent-tools.js +129 -0
  57. package/dist/protocols/mcp/index.d.ts +5 -0
  58. package/dist/protocols/mcp/index.js +11 -0
  59. package/dist/protocols/mcp/server.d.ts +31 -0
  60. package/dist/protocols/mcp/server.js +248 -0
  61. package/dist/protocols/mcp/types.d.ts +92 -0
  62. package/dist/protocols/mcp/types.js +17 -0
  63. package/dist/publish/index.d.ts +45 -0
  64. package/dist/publish/index.js +350 -0
  65. package/dist/schema/oad.d.ts +682 -65
  66. package/dist/schema/oad.js +36 -3
  67. package/dist/security/approval.d.ts +36 -0
  68. package/dist/security/approval.js +113 -0
  69. package/dist/security/index.d.ts +4 -0
  70. package/dist/security/index.js +8 -0
  71. package/dist/security/keys.d.ts +16 -0
  72. package/dist/security/keys.js +117 -0
  73. package/dist/studio/server.d.ts +63 -0
  74. package/dist/studio/server.js +625 -0
  75. package/dist/studio-ui/index.html +662 -0
  76. package/dist/telemetry/index.d.ts +93 -0
  77. package/dist/telemetry/index.js +285 -0
  78. package/package.json +5 -3
  79. package/scripts/install.ps1 +31 -0
  80. package/scripts/install.sh +40 -0
  81. package/src/channels/email.ts +351 -177
  82. package/src/channels/feishu.ts +349 -236
  83. package/src/channels/websocket.ts +399 -87
  84. package/src/channels/wechat.ts +329 -149
  85. package/src/cli.ts +783 -12
  86. package/src/core/a2a.ts +60 -0
  87. package/src/core/agent.ts +125 -0
  88. package/src/core/runtime.ts +127 -0
  89. package/src/core/sandbox.ts +143 -0
  90. package/src/core/workflow-graph.ts +365 -0
  91. package/src/doctor.ts +156 -0
  92. package/src/eval/index.ts +211 -0
  93. package/src/eval/suites/basic.json +16 -0
  94. package/src/eval/suites/memory.json +12 -0
  95. package/src/eval/suites/safety.json +14 -0
  96. package/src/index.ts +58 -6
  97. package/src/plugins/content-filter.ts +23 -0
  98. package/src/plugins/index.ts +133 -2
  99. package/src/plugins/logger.ts +18 -0
  100. package/src/plugins/rate-limiter.ts +38 -0
  101. package/src/protocols/a2a/client.ts +132 -0
  102. package/src/protocols/a2a/index.ts +8 -0
  103. package/src/protocols/a2a/server.ts +333 -0
  104. package/src/protocols/a2a/types.ts +88 -0
  105. package/src/protocols/a2a/utils.ts +50 -0
  106. package/src/protocols/agui/client.ts +83 -0
  107. package/src/protocols/agui/index.ts +4 -0
  108. package/src/protocols/agui/server.ts +218 -0
  109. package/src/protocols/agui/types.ts +153 -0
  110. package/src/protocols/index.ts +2 -0
  111. package/src/protocols/mcp/agent-tools.ts +134 -0
  112. package/src/protocols/mcp/index.ts +8 -0
  113. package/src/protocols/mcp/server.ts +262 -0
  114. package/src/protocols/mcp/types.ts +69 -0
  115. package/src/publish/index.ts +376 -0
  116. package/src/schema/oad.ts +39 -2
  117. package/src/security/approval.ts +131 -0
  118. package/src/security/index.ts +3 -0
  119. package/src/security/keys.ts +87 -0
  120. package/src/studio/server.ts +629 -0
  121. package/src/studio-ui/index.html +662 -0
  122. package/src/telemetry/index.ts +324 -0
  123. package/src/types/agent-workstation.d.ts +2 -0
  124. package/tests/a2a-protocol.test.ts +285 -0
  125. package/tests/agui-protocol.test.ts +246 -0
  126. package/tests/channels/discord.test.ts +79 -0
  127. package/tests/channels/email.test.ts +148 -0
  128. package/tests/channels/feishu.test.ts +123 -0
  129. package/tests/channels/telegram.test.ts +129 -0
  130. package/tests/channels/websocket.test.ts +53 -0
  131. package/tests/channels/wechat.test.ts +170 -0
  132. package/tests/chat-cli.test.ts +160 -0
  133. package/tests/daemon.test.ts +135 -0
  134. package/tests/deepbrain-wire.test.ts +234 -0
  135. package/tests/doctor.test.ts +38 -0
  136. package/tests/eval.test.ts +173 -0
  137. package/tests/init-role.test.ts +124 -0
  138. package/tests/mcp-client.test.ts +92 -0
  139. package/tests/mcp-server.test.ts +178 -0
  140. package/tests/plugin-a2a-enhanced.test.ts +230 -0
  141. package/tests/publish.test.ts +231 -0
  142. package/tests/scheduler.test.ts +200 -0
  143. package/tests/security-enhanced.test.ts +233 -0
  144. package/tests/skill-learner.test.ts +161 -0
  145. package/tests/studio.test.ts +229 -0
  146. package/tests/subagent.test.ts +63 -0
  147. package/tests/telemetry.test.ts +186 -0
  148. package/tests/tools/builtin-extended.test.ts +138 -0
  149. package/tests/workflow-graph.test.ts +279 -0
  150. package/tutorial/customer-service-agent/README.md +612 -0
  151. package/tutorial/customer-service-agent/SOUL.md +26 -0
  152. package/tutorial/customer-service-agent/agent.yaml +63 -0
  153. package/tutorial/customer-service-agent/package.json +19 -0
  154. package/tutorial/customer-service-agent/src/index.ts +69 -0
  155. package/tutorial/customer-service-agent/src/skills/faq.ts +27 -0
  156. package/tutorial/customer-service-agent/src/skills/ticket.ts +22 -0
  157. package/tutorial/customer-service-agent/tsconfig.json +14 -0
@@ -35,6 +35,8 @@ var __importStar = (this && this.__importStar) || (function () {
35
35
  Object.defineProperty(exports, "__esModule", { value: true });
36
36
  exports.FeishuChannel = void 0;
37
37
  const index_1 = require("./index");
38
+ const http = __importStar(require("http"));
39
+ const https = __importStar(require("https"));
38
40
  class FeishuChannel extends index_1.BaseChannel {
39
41
  type = 'feishu';
40
42
  config;
@@ -48,7 +50,7 @@ class FeishuChannel extends index_1.BaseChannel {
48
50
  appSecret: config.appSecret ?? process.env.FEISHU_APP_SECRET ?? '',
49
51
  verificationToken: config.verificationToken ?? process.env.FEISHU_VERIFICATION_TOKEN ?? '',
50
52
  encryptKey: config.encryptKey ?? process.env.FEISHU_ENCRYPT_KEY,
51
- port: config.port ?? 3002,
53
+ port: config.port ?? 8081,
52
54
  apiBase: config.apiBase ?? 'https://open.feishu.cn',
53
55
  };
54
56
  }
@@ -57,163 +59,260 @@ class FeishuChannel extends index_1.BaseChannel {
57
59
  console.warn('[FeishuChannel] Missing appId/appSecret. Set FEISHU_APP_ID and FEISHU_APP_SECRET.');
58
60
  return;
59
61
  }
60
- const express = (await Promise.resolve().then(() => __importStar(require('express')))).default;
61
- const app = express();
62
- app.use(express.json());
63
- // Event subscription endpoint
64
- app.post('/feishu/event', async (req, res) => {
62
+ this.server = http.createServer(async (req, res) => {
63
+ if (req.method === 'GET' && req.url === '/health') {
64
+ res.writeHead(200, { 'Content-Type': 'application/json' });
65
+ res.end(JSON.stringify({ status: 'ok', channel: 'feishu' }));
66
+ return;
67
+ }
68
+ if (req.method !== 'POST') {
69
+ res.writeHead(404);
70
+ res.end();
71
+ return;
72
+ }
65
73
  try {
66
- const body = req.body;
67
- // URL verification challenge
68
- if (body.type === 'url_verification') {
69
- res.json({ challenge: body.challenge });
70
- return;
71
- }
72
- // Deduplicate events
73
- const eventId = body.header?.event_id;
74
- if (eventId && this.processedEvents.has(eventId)) {
75
- res.json({ ok: true });
76
- return;
77
- }
78
- if (eventId) {
79
- this.processedEvents.add(eventId);
80
- // Prune old events (keep last 1000)
81
- if (this.processedEvents.size > 1000) {
82
- const arr = [...this.processedEvents];
83
- this.processedEvents = new Set(arr.slice(-500));
84
- }
85
- }
86
- // Verify token
87
- if (this.config.verificationToken && body.header?.token !== this.config.verificationToken) {
88
- res.status(403).json({ error: 'Invalid verification token' });
89
- return;
90
- }
91
- // Handle im.message.receive_v1
92
- const event = body.event;
93
- if (body.header?.event_type === 'im.message.receive_v1' && this.handler) {
94
- const msgBody = event?.message;
95
- if (!msgBody) {
96
- res.json({ ok: true });
97
- return;
98
- }
99
- // Only handle text messages for now
100
- const msgType = msgBody.message_type;
101
- let content = '';
102
- if (msgType === 'text') {
103
- try {
104
- const parsed = JSON.parse(msgBody.content);
105
- content = parsed.text ?? '';
106
- }
107
- catch {
108
- content = msgBody.content ?? '';
109
- }
110
- }
111
- else {
112
- // Acknowledge non-text silently
113
- res.json({ ok: true });
114
- return;
115
- }
116
- // Strip @bot mentions
117
- content = content.replace(/@_user_\d+/g, '').trim();
118
- if (!content) {
119
- res.json({ ok: true });
120
- return;
121
- }
122
- const chatId = msgBody.chat_id;
123
- const senderId = event.sender?.sender_id?.open_id ?? 'unknown';
124
- const msg = {
125
- id: `feishu_${msgBody.message_id}`,
126
- role: 'user',
127
- content,
128
- timestamp: parseInt(msgBody.create_time, 10) || Date.now(),
129
- metadata: {
130
- sessionId: `feishu_${chatId}`,
131
- chatId,
132
- userId: senderId,
133
- platform: 'feishu',
134
- messageId: msgBody.message_id,
135
- chatType: msgBody.chat_type, // 'p2p' or 'group'
136
- },
137
- };
138
- const response = await this.handler(msg);
139
- await this.sendTextMessage(chatId, response.content);
140
- }
141
- res.json({ ok: true });
74
+ const body = await this.readBody(req);
75
+ const parsed = JSON.parse(body);
76
+ await this.handleEvent(parsed, res);
142
77
  }
143
78
  catch (err) {
144
- console.error('[FeishuChannel] Error handling event:', err);
145
- res.status(500).json({ error: 'Internal error' });
79
+ console.error('[FeishuChannel] Error:', err);
80
+ res.writeHead(500, { 'Content-Type': 'application/json' });
81
+ res.end(JSON.stringify({ error: 'Internal error' }));
146
82
  }
147
83
  });
148
- app.get('/health', (_req, res) => {
149
- res.json({ status: 'ok', channel: 'feishu' });
150
- });
151
- this.server = app.listen(this.config.port, () => {
152
- console.log(`[FeishuChannel] Listening on port ${this.config.port}`);
84
+ return new Promise((resolve) => {
85
+ this.server.listen(this.config.port, () => {
86
+ console.log(`[FeishuChannel] Listening on port ${this.config.port}`);
87
+ resolve();
88
+ });
153
89
  });
154
90
  }
155
91
  async stop() {
156
- if (this.server) {
157
- this.server.close();
92
+ return new Promise((resolve, reject) => {
93
+ if (!this.server)
94
+ return resolve();
95
+ this.server.close((err) => (err ? reject(err) : resolve()));
158
96
  this.server = null;
97
+ });
98
+ }
99
+ /** Handle Feishu event */
100
+ async handleEvent(body, res) {
101
+ // URL verification challenge
102
+ if (body.type === 'url_verification') {
103
+ res.writeHead(200, { 'Content-Type': 'application/json' });
104
+ res.end(JSON.stringify({ challenge: body.challenge }));
105
+ return;
106
+ }
107
+ // Deduplicate events
108
+ const eventId = body.header?.event_id;
109
+ if (eventId && this.processedEvents.has(eventId)) {
110
+ res.writeHead(200, { 'Content-Type': 'application/json' });
111
+ res.end(JSON.stringify({ ok: true }));
112
+ return;
113
+ }
114
+ if (eventId) {
115
+ this.processedEvents.add(eventId);
116
+ if (this.processedEvents.size > 1000) {
117
+ const arr = [...this.processedEvents];
118
+ this.processedEvents = new Set(arr.slice(-500));
119
+ }
120
+ }
121
+ // Verify token
122
+ if (this.config.verificationToken && body.header?.token !== this.config.verificationToken) {
123
+ res.writeHead(403, { 'Content-Type': 'application/json' });
124
+ res.end(JSON.stringify({ error: 'Invalid verification token' }));
125
+ return;
126
+ }
127
+ // Handle im.message.receive_v1
128
+ const event = body.event;
129
+ if (body.header?.event_type === 'im.message.receive_v1' && this.handler) {
130
+ const msgBody = event?.message;
131
+ if (!msgBody) {
132
+ res.writeHead(200, { 'Content-Type': 'application/json' });
133
+ res.end(JSON.stringify({ ok: true }));
134
+ return;
135
+ }
136
+ const msgType = msgBody.message_type;
137
+ let content = '';
138
+ if (msgType === 'text') {
139
+ try {
140
+ const parsed = JSON.parse(msgBody.content);
141
+ content = parsed.text ?? '';
142
+ }
143
+ catch {
144
+ content = msgBody.content ?? '';
145
+ }
146
+ }
147
+ else {
148
+ res.writeHead(200, { 'Content-Type': 'application/json' });
149
+ res.end(JSON.stringify({ ok: true }));
150
+ return;
151
+ }
152
+ // Strip @bot mentions
153
+ content = content.replace(/@_user_\d+/g, '').trim();
154
+ if (!content) {
155
+ res.writeHead(200, { 'Content-Type': 'application/json' });
156
+ res.end(JSON.stringify({ ok: true }));
157
+ return;
158
+ }
159
+ const chatId = msgBody.chat_id;
160
+ const senderId = event.sender?.sender_id?.open_id ?? 'unknown';
161
+ const msg = {
162
+ id: `feishu_${msgBody.message_id}`,
163
+ role: 'user',
164
+ content,
165
+ timestamp: parseInt(msgBody.create_time, 10) || Date.now(),
166
+ metadata: {
167
+ sessionId: `feishu_${chatId}`,
168
+ chatId,
169
+ userId: senderId,
170
+ platform: 'feishu',
171
+ messageId: msgBody.message_id,
172
+ chatType: msgBody.chat_type,
173
+ },
174
+ };
175
+ // Don't block the response
176
+ res.writeHead(200, { 'Content-Type': 'application/json' });
177
+ res.end(JSON.stringify({ ok: true }));
178
+ try {
179
+ const response = await this.handler(msg);
180
+ await this.sendTextMessage(chatId, response.content);
181
+ }
182
+ catch (err) {
183
+ console.error('[FeishuChannel] Handler error:', err);
184
+ }
185
+ return;
159
186
  }
187
+ res.writeHead(200, { 'Content-Type': 'application/json' });
188
+ res.end(JSON.stringify({ ok: true }));
189
+ }
190
+ /** Parse Feishu event body (exported for testing) */
191
+ static parseEventBody(body) {
192
+ if (body.type === 'url_verification') {
193
+ return { type: 'url_verification', challenge: body.challenge };
194
+ }
195
+ const eventType = body.header?.event_type;
196
+ const event = body.event;
197
+ if (eventType === 'im.message.receive_v1' && event?.message) {
198
+ const msgBody = event.message;
199
+ let content = '';
200
+ if (msgBody.message_type === 'text') {
201
+ try {
202
+ const parsed = JSON.parse(msgBody.content);
203
+ content = parsed.text ?? '';
204
+ }
205
+ catch {
206
+ content = msgBody.content ?? '';
207
+ }
208
+ }
209
+ return {
210
+ type: 'message',
211
+ eventType,
212
+ content: content.replace(/@_user_\d+/g, '').trim(),
213
+ chatId: msgBody.chat_id,
214
+ senderId: event.sender?.sender_id?.open_id,
215
+ messageId: msgBody.message_id,
216
+ };
217
+ }
218
+ return { type: 'unknown', eventType };
160
219
  }
161
220
  /** Get tenant access token (cached) */
162
221
  async getAccessToken() {
163
222
  if (this.tokenCache && Date.now() < this.tokenCache.expiresAt) {
164
223
  return this.tokenCache.token;
165
224
  }
166
- const resp = await fetch(`${this.config.apiBase}/open-apis/auth/v3/tenant_access_token/internal`, {
167
- method: 'POST',
168
- headers: { 'Content-Type': 'application/json' },
169
- body: JSON.stringify({
170
- app_id: this.config.appId,
171
- app_secret: this.config.appSecret,
172
- }),
225
+ const body = JSON.stringify({
226
+ app_id: this.config.appId,
227
+ app_secret: this.config.appSecret,
173
228
  });
174
- const data = await resp.json();
229
+ const result = await this.httpsPost(`${this.config.apiBase}/open-apis/auth/v3/tenant_access_token/internal`, body);
230
+ const data = JSON.parse(result);
175
231
  if (data.code !== 0) {
176
- throw new Error(`[FeishuChannel] Failed to get access token: ${JSON.stringify(data)}`);
232
+ throw new Error(`[FeishuChannel] Failed to get access token: ${result}`);
177
233
  }
178
234
  this.tokenCache = {
179
235
  token: data.tenant_access_token,
180
- expiresAt: Date.now() + (data.expire - 60) * 1000, // refresh 60s early
236
+ expiresAt: Date.now() + (data.expire - 60) * 1000,
181
237
  };
182
238
  return this.tokenCache.token;
183
239
  }
184
240
  /** Send a text message to a chat */
185
241
  async sendTextMessage(chatId, text) {
186
242
  const token = await this.getAccessToken();
187
- const resp = await fetch(`${this.config.apiBase}/open-apis/im/v1/messages?receive_id_type=chat_id`, {
188
- method: 'POST',
189
- headers: {
190
- 'Content-Type': 'application/json',
191
- 'Authorization': `Bearer ${token}`,
192
- },
193
- body: JSON.stringify({
194
- receive_id: chatId,
195
- msg_type: 'text',
196
- content: JSON.stringify({ text }),
197
- }),
243
+ const body = JSON.stringify({
244
+ receive_id: chatId,
245
+ msg_type: 'text',
246
+ content: JSON.stringify({ text }),
198
247
  });
199
- if (!resp.ok) {
200
- console.error('[FeishuChannel] Failed to send message:', await resp.text());
201
- }
248
+ const url = `${this.config.apiBase}/open-apis/im/v1/messages?receive_id_type=chat_id`;
249
+ await this.httpsPostWithAuth(url, body, token);
202
250
  }
203
251
  /** Send an interactive card message */
204
252
  async sendCardMessage(chatId, card) {
205
253
  const token = await this.getAccessToken();
206
- await fetch(`${this.config.apiBase}/open-apis/im/v1/messages?receive_id_type=chat_id`, {
207
- method: 'POST',
208
- headers: {
209
- 'Content-Type': 'application/json',
210
- 'Authorization': `Bearer ${token}`,
211
- },
212
- body: JSON.stringify({
213
- receive_id: chatId,
214
- msg_type: 'interactive',
215
- content: JSON.stringify(card),
216
- }),
254
+ const body = JSON.stringify({
255
+ receive_id: chatId,
256
+ msg_type: 'interactive',
257
+ content: JSON.stringify(card),
258
+ });
259
+ const url = `${this.config.apiBase}/open-apis/im/v1/messages?receive_id_type=chat_id`;
260
+ await this.httpsPostWithAuth(url, body, token);
261
+ }
262
+ /** Read request body */
263
+ readBody(req) {
264
+ return new Promise((resolve, reject) => {
265
+ const chunks = [];
266
+ req.on('data', (chunk) => chunks.push(chunk));
267
+ req.on('end', () => resolve(Buffer.concat(chunks).toString('utf-8')));
268
+ req.on('error', reject);
269
+ });
270
+ }
271
+ /** HTTPS POST */
272
+ httpsPost(url, body) {
273
+ return new Promise((resolve, reject) => {
274
+ const parsed = new URL(url);
275
+ const req = https.request({
276
+ hostname: parsed.hostname,
277
+ path: parsed.pathname + parsed.search,
278
+ method: 'POST',
279
+ headers: {
280
+ 'Content-Type': 'application/json',
281
+ 'Content-Length': Buffer.byteLength(body),
282
+ },
283
+ }, (res) => {
284
+ const chunks = [];
285
+ res.on('data', (chunk) => chunks.push(chunk));
286
+ res.on('end', () => resolve(Buffer.concat(chunks).toString()));
287
+ res.on('error', reject);
288
+ });
289
+ req.on('error', reject);
290
+ req.write(body);
291
+ req.end();
292
+ });
293
+ }
294
+ /** HTTPS POST with Bearer auth */
295
+ httpsPostWithAuth(url, body, token) {
296
+ return new Promise((resolve, reject) => {
297
+ const parsed = new URL(url);
298
+ const req = https.request({
299
+ hostname: parsed.hostname,
300
+ path: parsed.pathname + parsed.search,
301
+ method: 'POST',
302
+ headers: {
303
+ 'Content-Type': 'application/json',
304
+ 'Content-Length': Buffer.byteLength(body),
305
+ 'Authorization': `Bearer ${token}`,
306
+ },
307
+ }, (res) => {
308
+ const chunks = [];
309
+ res.on('data', (chunk) => chunks.push(chunk));
310
+ res.on('end', () => resolve(Buffer.concat(chunks).toString()));
311
+ res.on('error', reject);
312
+ });
313
+ req.on('error', reject);
314
+ req.write(body);
315
+ req.end();
217
316
  });
218
317
  }
219
318
  }
@@ -1,15 +1,58 @@
1
1
  import { BaseChannel } from './index';
2
2
  /**
3
- * WebSocket channelreal-time bidirectional communication.
3
+ * WebSocket Channelv1.1.0
4
+ *
5
+ * Enhanced with:
6
+ * - Room support (multiple clients in a room)
7
+ * - Heartbeat/ping-pong to detect disconnected clients
8
+ * - Reconnection handling (session persistence)
9
+ * - Binary message support
10
+ * - Connection authentication (optional token in query string)
4
11
  */
12
+ export interface WebSocketChannelConfig {
13
+ port?: number;
14
+ /** Heartbeat interval in ms (default: 30000) */
15
+ heartbeatInterval?: number;
16
+ /** Valid auth tokens (if empty, no auth required) */
17
+ authTokens?: string[];
18
+ /** Max clients per room (default: 100) */
19
+ maxClientsPerRoom?: number;
20
+ }
5
21
  export declare class WebSocketChannel extends BaseChannel {
6
22
  readonly type = "websocket";
7
23
  private wss;
8
- private port;
24
+ private config;
9
25
  private clients;
10
- constructor(port?: number);
26
+ private rooms;
27
+ private heartbeatTimer;
28
+ constructor(configOrPort?: number | WebSocketChannelConfig);
11
29
  start(): Promise<void>;
12
30
  stop(): Promise<void>;
31
+ /** Handle text (JSON) messages */
32
+ private handleTextMessage;
33
+ /** Handle binary messages */
34
+ private handleBinaryMessage;
35
+ /** Join a room */
36
+ joinRoom(sessionId: string, roomId: string): boolean;
37
+ /** Leave a room */
38
+ leaveRoom(sessionId: string, roomId: string): void;
39
+ /** Remove client completely */
40
+ private removeClient;
41
+ /** Get room member session IDs */
42
+ getRoomMembers(roomId: string): string[];
43
+ /** Get all rooms */
44
+ getRooms(): string[];
45
+ /** Broadcast to all clients */
13
46
  broadcast(content: string): void;
47
+ /** Broadcast to all clients in a room */
48
+ broadcastToRoom(roomId: string, data: any, excludeSessionId?: string): void;
49
+ /** Send to specific session */
50
+ sendToSession(sessionId: string, data: any): boolean;
51
+ /** Get connection stats */
52
+ getStats(): {
53
+ clients: number;
54
+ rooms: number;
55
+ roomDetails: Record<string, number>;
56
+ };
14
57
  }
15
58
  //# sourceMappingURL=websocket.d.ts.map