opc-agent 1.3.2 → 2.0.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 (226) hide show
  1. package/.github/ISSUE_TEMPLATE/bug_report.md +20 -0
  2. package/.github/ISSUE_TEMPLATE/feature_request.md +14 -0
  3. package/.github/PULL_REQUEST_TEMPLATE.md +13 -0
  4. package/.github/workflows/ci.yml +24 -0
  5. package/CHANGELOG.md +48 -63
  6. package/CONTRIBUTING.md +21 -60
  7. package/README.md +284 -348
  8. package/README.zh-CN.md +415 -415
  9. package/dist/channels/slack.js +93 -10
  10. package/dist/channels/telegram.d.ts +30 -9
  11. package/dist/channels/telegram.js +125 -33
  12. package/dist/channels/web.d.ts +10 -0
  13. package/dist/channels/web.js +33 -2
  14. package/dist/cli.js +667 -65
  15. package/dist/core/agent.d.ts +23 -0
  16. package/dist/core/agent.js +120 -3
  17. package/dist/core/runtime.d.ts +5 -0
  18. package/dist/core/runtime.js +71 -0
  19. package/dist/core/scheduler.d.ts +52 -0
  20. package/dist/core/scheduler.js +168 -0
  21. package/dist/core/subagent.d.ts +28 -0
  22. package/dist/core/subagent.js +65 -0
  23. package/dist/daemon.d.ts +3 -0
  24. package/dist/daemon.js +134 -0
  25. package/dist/deploy/hermes.js +22 -22
  26. package/dist/deploy/openclaw.js +31 -40
  27. package/dist/index.d.ts +10 -10
  28. package/dist/index.js +22 -15
  29. package/dist/providers/index.d.ts +6 -2
  30. package/dist/providers/index.js +22 -9
  31. package/dist/schema/oad.d.ts +180 -6
  32. package/dist/schema/oad.js +12 -1
  33. package/dist/skills/auto-learn.d.ts +28 -0
  34. package/dist/skills/auto-learn.js +257 -0
  35. package/dist/templates/code-reviewer.d.ts +0 -8
  36. package/dist/templates/code-reviewer.js +5 -9
  37. package/dist/templates/customer-service.d.ts +0 -8
  38. package/dist/templates/customer-service.js +2 -6
  39. package/dist/templates/data-analyst.d.ts +0 -8
  40. package/dist/templates/data-analyst.js +5 -9
  41. package/dist/templates/knowledge-base.d.ts +0 -8
  42. package/dist/templates/knowledge-base.js +2 -6
  43. package/dist/templates/sales-assistant.d.ts +0 -8
  44. package/dist/templates/sales-assistant.js +4 -8
  45. package/dist/templates/teacher.d.ts +0 -8
  46. package/dist/templates/teacher.js +6 -10
  47. package/dist/tools/builtin/datetime.d.ts +3 -0
  48. package/dist/tools/builtin/datetime.js +44 -0
  49. package/dist/tools/builtin/file.d.ts +3 -0
  50. package/dist/tools/builtin/file.js +151 -0
  51. package/dist/tools/builtin/index.d.ts +15 -0
  52. package/dist/tools/builtin/index.js +30 -0
  53. package/dist/tools/builtin/shell.d.ts +3 -0
  54. package/dist/tools/builtin/shell.js +43 -0
  55. package/dist/tools/builtin/web.d.ts +3 -0
  56. package/dist/tools/builtin/web.js +37 -0
  57. package/dist/tools/mcp-client.d.ts +24 -0
  58. package/dist/tools/mcp-client.js +119 -0
  59. package/dist/traces/index.d.ts +49 -0
  60. package/dist/traces/index.js +102 -0
  61. package/docs/.vitepress/config.ts +103 -103
  62. package/docs/api/cli.md +48 -48
  63. package/docs/api/oad-schema.md +64 -64
  64. package/docs/api/sdk.md +80 -80
  65. package/docs/guide/concepts.md +51 -51
  66. package/docs/guide/configuration.md +79 -79
  67. package/docs/guide/deployment.md +42 -42
  68. package/docs/guide/getting-started.md +44 -44
  69. package/docs/guide/templates.md +28 -28
  70. package/docs/guide/testing.md +84 -84
  71. package/docs/index.md +27 -27
  72. package/docs/zh/api/cli.md +54 -54
  73. package/docs/zh/api/oad-schema.md +87 -87
  74. package/docs/zh/api/sdk.md +102 -102
  75. package/docs/zh/guide/concepts.md +104 -104
  76. package/docs/zh/guide/configuration.md +135 -135
  77. package/docs/zh/guide/deployment.md +81 -81
  78. package/docs/zh/guide/getting-started.md +82 -82
  79. package/docs/zh/guide/templates.md +84 -84
  80. package/docs/zh/guide/testing.md +88 -88
  81. package/docs/zh/index.md +27 -27
  82. package/examples/README.md +22 -0
  83. package/examples/basic-agent.ts +90 -0
  84. package/examples/brain-integration.ts +71 -0
  85. package/examples/customer-service-demo/README.md +90 -90
  86. package/examples/customer-service-demo/oad.yaml +107 -107
  87. package/examples/multi-channel.ts +74 -0
  88. package/package.json +1 -1
  89. package/src/analytics/index.ts +66 -66
  90. package/src/channels/discord.ts +192 -192
  91. package/src/channels/email.ts +177 -177
  92. package/src/channels/feishu.ts +236 -236
  93. package/src/channels/index.ts +15 -15
  94. package/src/channels/slack.ts +217 -160
  95. package/src/channels/telegram.ts +155 -33
  96. package/src/channels/voice.ts +106 -106
  97. package/src/channels/web.ts +38 -2
  98. package/src/channels/webhook.ts +199 -199
  99. package/src/channels/websocket.ts +87 -87
  100. package/src/channels/wechat.ts +149 -149
  101. package/src/cli.ts +697 -63
  102. package/src/core/a2a.ts +143 -143
  103. package/src/core/agent.ts +146 -3
  104. package/src/core/analytics-engine.ts +186 -186
  105. package/src/core/auth.ts +57 -57
  106. package/src/core/cache.ts +141 -141
  107. package/src/core/compose.ts +77 -77
  108. package/src/core/config.ts +14 -14
  109. package/src/core/errors.ts +148 -148
  110. package/src/core/hitl.ts +138 -138
  111. package/src/core/logger.ts +57 -57
  112. package/src/core/orchestrator.ts +215 -215
  113. package/src/core/performance.ts +187 -187
  114. package/src/core/rate-limiter.ts +128 -128
  115. package/src/core/room.ts +109 -109
  116. package/src/core/runtime.ts +230 -152
  117. package/src/core/sandbox.ts +101 -101
  118. package/src/core/scheduler.ts +187 -0
  119. package/src/core/security.ts +171 -171
  120. package/src/core/subagent.ts +98 -0
  121. package/src/core/types.ts +68 -68
  122. package/src/core/versioning.ts +106 -106
  123. package/src/core/watch.ts +178 -178
  124. package/src/core/workflow.ts +235 -235
  125. package/src/daemon.ts +96 -0
  126. package/src/deploy/hermes.ts +156 -156
  127. package/src/deploy/openclaw.ts +190 -200
  128. package/src/i18n/index.ts +216 -216
  129. package/src/index.ts +14 -10
  130. package/src/memory/deepbrain.ts +108 -108
  131. package/src/memory/index.ts +34 -34
  132. package/src/plugins/index.ts +208 -208
  133. package/src/providers/index.ts +354 -331
  134. package/src/schema/oad.ts +14 -2
  135. package/src/skills/auto-learn.ts +262 -0
  136. package/src/skills/base.ts +16 -16
  137. package/src/skills/document.ts +100 -100
  138. package/src/skills/http.ts +35 -35
  139. package/src/skills/index.ts +27 -27
  140. package/src/skills/scheduler.ts +80 -80
  141. package/src/skills/webhook-trigger.ts +59 -59
  142. package/src/templates/code-reviewer.ts +30 -34
  143. package/src/templates/customer-service.ts +76 -80
  144. package/src/templates/data-analyst.ts +66 -70
  145. package/src/templates/executive-assistant.ts +71 -71
  146. package/src/templates/financial-advisor.ts +60 -60
  147. package/src/templates/knowledge-base.ts +27 -31
  148. package/src/templates/legal-assistant.ts +71 -71
  149. package/src/templates/sales-assistant.ts +75 -79
  150. package/src/templates/teacher.ts +75 -79
  151. package/src/testing/index.ts +181 -181
  152. package/src/tools/builtin/datetime.ts +41 -0
  153. package/src/tools/builtin/file.ts +107 -0
  154. package/src/tools/builtin/index.ts +28 -0
  155. package/src/tools/builtin/shell.ts +43 -0
  156. package/src/tools/builtin/web.ts +35 -0
  157. package/src/tools/calculator.ts +73 -73
  158. package/src/tools/datetime.ts +149 -149
  159. package/src/tools/json-transform.ts +187 -187
  160. package/src/tools/mcp-client.ts +131 -0
  161. package/src/tools/mcp.ts +76 -76
  162. package/src/tools/text-analysis.ts +116 -116
  163. package/src/traces/index.ts +132 -0
  164. package/templates/Dockerfile +15 -15
  165. package/templates/code-reviewer/README.md +27 -27
  166. package/templates/code-reviewer/oad.yaml +41 -41
  167. package/templates/customer-service/README.md +22 -22
  168. package/templates/customer-service/oad.yaml +36 -36
  169. package/templates/docker-compose.yml +21 -21
  170. package/templates/ecommerce-assistant/README.md +45 -45
  171. package/templates/ecommerce-assistant/oad.yaml +47 -47
  172. package/templates/knowledge-base/README.md +28 -28
  173. package/templates/knowledge-base/oad.yaml +38 -38
  174. package/templates/sales-assistant/README.md +26 -26
  175. package/templates/sales-assistant/oad.yaml +43 -43
  176. package/templates/tech-support/README.md +43 -43
  177. package/templates/tech-support/oad.yaml +45 -45
  178. package/test-agent/Dockerfile +9 -0
  179. package/test-agent/README.md +50 -0
  180. package/test-agent/agent.yaml +23 -0
  181. package/test-agent/docker-compose.yml +11 -0
  182. package/test-agent/oad.yaml +31 -0
  183. package/test-agent/package-lock.json +1492 -0
  184. package/test-agent/package.json +18 -0
  185. package/test-agent/src/index.ts +24 -0
  186. package/test-agent/src/skills/echo.ts +15 -0
  187. package/test-agent/tsconfig.json +25 -0
  188. package/tests/a2a.test.ts +66 -66
  189. package/tests/agent.test.ts +72 -72
  190. package/tests/analytics.test.ts +50 -50
  191. package/tests/auto-learn.test.ts +105 -0
  192. package/tests/builtin-tools.test.ts +83 -0
  193. package/tests/channel.test.ts +39 -39
  194. package/tests/cli.test.ts +46 -0
  195. package/tests/e2e.test.ts +134 -134
  196. package/tests/errors.test.ts +83 -83
  197. package/tests/hitl.test.ts +71 -71
  198. package/tests/i18n.test.ts +41 -41
  199. package/tests/mcp.test.ts +54 -54
  200. package/tests/oad.test.ts +68 -68
  201. package/tests/performance.test.ts +115 -115
  202. package/tests/plugin.test.ts +74 -74
  203. package/tests/room.test.ts +106 -106
  204. package/tests/runtime.test.ts +42 -42
  205. package/tests/sandbox.test.ts +46 -46
  206. package/tests/security.test.ts +60 -60
  207. package/tests/subagent.test.ts +130 -0
  208. package/tests/telegram-discord.test.ts +60 -0
  209. package/tests/templates.test.ts +77 -77
  210. package/tests/v070.test.ts +76 -76
  211. package/tests/versioning.test.ts +75 -75
  212. package/tests/voice.test.ts +61 -61
  213. package/tests/webhook.test.ts +29 -29
  214. package/tests/workflow.test.ts +143 -143
  215. package/tsconfig.json +19 -19
  216. package/vitest.config.ts +9 -9
  217. package/dist/core/dashboard.d.ts +0 -35
  218. package/dist/core/dashboard.js +0 -157
  219. package/dist/core/priority.d.ts +0 -52
  220. package/dist/core/priority.js +0 -102
  221. package/src/core/dashboard.ts +0 -219
  222. package/src/core/priority.ts +0 -140
  223. package/src/dtv/data.ts +0 -29
  224. package/src/dtv/trust.ts +0 -43
  225. package/src/dtv/value.ts +0 -47
  226. package/src/marketplace/index.ts +0 -223
@@ -1,87 +1,87 @@
1
- import type { Message } from '../core/types';
2
- import { BaseChannel } from './index';
3
- import { WebSocketServer, type WebSocket } from 'ws';
4
-
5
- /**
6
- * WebSocket channel — real-time bidirectional communication.
7
- */
8
- export class WebSocketChannel extends BaseChannel {
9
- readonly type = 'websocket';
10
- private wss: WebSocketServer | null = null;
11
- private port: number;
12
- private clients: Set<WebSocket> = new Set();
13
-
14
- constructor(port: number = 3002) {
15
- super();
16
- this.port = port;
17
- }
18
-
19
- async start(): Promise<void> {
20
- return new Promise((resolve) => {
21
- this.wss = new WebSocketServer({ port: this.port });
22
-
23
- this.wss.on('connection', (ws) => {
24
- this.clients.add(ws);
25
-
26
- ws.on('message', async (data) => {
27
- if (!this.handler) return;
28
-
29
- try {
30
- const parsed = JSON.parse(data.toString());
31
- const msg: Message = {
32
- id: parsed.id ?? `ws_${Date.now()}_${Math.random().toString(36).slice(2, 8)}`,
33
- role: 'user',
34
- content: parsed.content ?? parsed.message ?? data.toString(),
35
- timestamp: Date.now(),
36
- metadata: {
37
- sessionId: parsed.sessionId ?? 'ws-default',
38
- platform: 'websocket',
39
- },
40
- };
41
-
42
- const response = await this.handler(msg);
43
- ws.send(JSON.stringify({
44
- id: response.id,
45
- content: response.content,
46
- timestamp: response.timestamp,
47
- }));
48
- } catch (err) {
49
- ws.send(JSON.stringify({ error: 'Invalid message format' }));
50
- }
51
- });
52
-
53
- ws.on('close', () => {
54
- this.clients.delete(ws);
55
- });
56
-
57
- ws.send(JSON.stringify({ type: 'connected', timestamp: Date.now() }));
58
- });
59
-
60
- this.wss.on('listening', () => {
61
- console.log(`[WebSocketChannel] Listening on port ${this.port}`);
62
- resolve();
63
- });
64
- });
65
- }
66
-
67
- async stop(): Promise<void> {
68
- for (const client of this.clients) {
69
- client.close();
70
- }
71
- this.clients.clear();
72
-
73
- return new Promise((resolve, reject) => {
74
- if (!this.wss) return resolve();
75
- this.wss.close((err) => (err ? reject(err) : resolve()));
76
- });
77
- }
78
-
79
- broadcast(content: string): void {
80
- const msg = JSON.stringify({ type: 'broadcast', content, timestamp: Date.now() });
81
- for (const client of this.clients) {
82
- if (client.readyState === 1) { // OPEN
83
- client.send(msg);
84
- }
85
- }
86
- }
87
- }
1
+ import type { Message } from '../core/types';
2
+ import { BaseChannel } from './index';
3
+ import { WebSocketServer, type WebSocket } from 'ws';
4
+
5
+ /**
6
+ * WebSocket channel — real-time bidirectional communication.
7
+ */
8
+ export class WebSocketChannel extends BaseChannel {
9
+ readonly type = 'websocket';
10
+ private wss: WebSocketServer | null = null;
11
+ private port: number;
12
+ private clients: Set<WebSocket> = new Set();
13
+
14
+ constructor(port: number = 3002) {
15
+ super();
16
+ this.port = port;
17
+ }
18
+
19
+ async start(): Promise<void> {
20
+ return new Promise((resolve) => {
21
+ this.wss = new WebSocketServer({ port: this.port });
22
+
23
+ this.wss.on('connection', (ws) => {
24
+ this.clients.add(ws);
25
+
26
+ ws.on('message', async (data) => {
27
+ if (!this.handler) return;
28
+
29
+ try {
30
+ const parsed = JSON.parse(data.toString());
31
+ const msg: Message = {
32
+ id: parsed.id ?? `ws_${Date.now()}_${Math.random().toString(36).slice(2, 8)}`,
33
+ role: 'user',
34
+ content: parsed.content ?? parsed.message ?? data.toString(),
35
+ timestamp: Date.now(),
36
+ metadata: {
37
+ sessionId: parsed.sessionId ?? 'ws-default',
38
+ platform: 'websocket',
39
+ },
40
+ };
41
+
42
+ const response = await this.handler(msg);
43
+ ws.send(JSON.stringify({
44
+ id: response.id,
45
+ content: response.content,
46
+ timestamp: response.timestamp,
47
+ }));
48
+ } catch (err) {
49
+ ws.send(JSON.stringify({ error: 'Invalid message format' }));
50
+ }
51
+ });
52
+
53
+ ws.on('close', () => {
54
+ this.clients.delete(ws);
55
+ });
56
+
57
+ ws.send(JSON.stringify({ type: 'connected', timestamp: Date.now() }));
58
+ });
59
+
60
+ this.wss.on('listening', () => {
61
+ console.log(`[WebSocketChannel] Listening on port ${this.port}`);
62
+ resolve();
63
+ });
64
+ });
65
+ }
66
+
67
+ async stop(): Promise<void> {
68
+ for (const client of this.clients) {
69
+ client.close();
70
+ }
71
+ this.clients.clear();
72
+
73
+ return new Promise((resolve, reject) => {
74
+ if (!this.wss) return resolve();
75
+ this.wss.close((err) => (err ? reject(err) : resolve()));
76
+ });
77
+ }
78
+
79
+ broadcast(content: string): void {
80
+ const msg = JSON.stringify({ type: 'broadcast', content, timestamp: Date.now() });
81
+ for (const client of this.clients) {
82
+ if (client.readyState === 1) { // OPEN
83
+ client.send(msg);
84
+ }
85
+ }
86
+ }
87
+ }
@@ -1,149 +1,149 @@
1
- import { BaseChannel } from './index';
2
- import type { Message } from '../core/types';
3
-
4
- /**
5
- * WeChat Channel (Stub) — v0.8.0
6
- * WeChat Official Account message handling, template messages, QR code login.
7
- */
8
-
9
- export interface WeChatChannelConfig {
10
- /** WeChat Official Account AppID */
11
- appId: string;
12
- /** WeChat Official Account AppSecret */
13
- appSecret: string;
14
- /** Verification token for message validation */
15
- token: string;
16
- /** AES encoding key for encrypted messages */
17
- encodingAESKey?: string;
18
- /** HTTP server port (default: 3002) */
19
- port?: number;
20
- }
21
-
22
- export interface WeChatMessage {
23
- toUserName: string;
24
- fromUserName: string;
25
- createTime: number;
26
- msgType: 'text' | 'image' | 'voice' | 'video' | 'event';
27
- content?: string;
28
- msgId?: string;
29
- event?: string;
30
- eventKey?: string;
31
- }
32
-
33
- export interface TemplateMessageData {
34
- toUser: string;
35
- templateId: string;
36
- url?: string;
37
- data: Record<string, { value: string; color?: string }>;
38
- }
39
-
40
- export class WeChatChannel extends BaseChannel {
41
- type = 'wechat';
42
- private config: WeChatChannelConfig;
43
- private accessToken: string | null = null;
44
- private tokenExpiry = 0;
45
-
46
- constructor(config: WeChatChannelConfig) {
47
- super();
48
- this.config = config;
49
- }
50
-
51
- async start(): Promise<void> {
52
- // TODO: Start HTTP server to receive WeChat push messages
53
- // 1. Verify signature on GET requests
54
- // 2. Parse XML messages on POST requests
55
- // 3. Route to handler and reply with XML
56
- }
57
-
58
- async stop(): Promise<void> {
59
- // TODO: Stop HTTP server
60
- }
61
-
62
- /** Get or refresh access token */
63
- async getAccessToken(): Promise<string> {
64
- if (this.accessToken && Date.now() < this.tokenExpiry) {
65
- return this.accessToken;
66
- }
67
-
68
- // TODO: Implement token refresh
69
- // const url = `https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid=${this.config.appId}&secret=${this.config.appSecret}`;
70
- // const res = await fetch(url);
71
- // const data = await res.json();
72
- // this.accessToken = data.access_token;
73
- // this.tokenExpiry = Date.now() + (data.expires_in - 300) * 1000;
74
-
75
- return this.accessToken ?? '';
76
- }
77
-
78
- /** Handle incoming WeChat message */
79
- async handleMessage(wxMsg: WeChatMessage): Promise<string> {
80
- if (wxMsg.msgType === 'event') {
81
- return this.handleEvent(wxMsg);
82
- }
83
-
84
- const message = this.wechatToMessage(wxMsg);
85
- if (this.handler) {
86
- const reply = await this.handler(message);
87
- return reply.content;
88
- }
89
- return '';
90
- }
91
-
92
- /** Handle WeChat events (subscribe, scan, etc.) */
93
- private handleEvent(wxMsg: WeChatMessage): string {
94
- switch (wxMsg.event) {
95
- case 'subscribe':
96
- return 'Welcome! How can I help you?';
97
- case 'SCAN':
98
- return `QR code scanned: ${wxMsg.eventKey}`;
99
- default:
100
- return '';
101
- }
102
- }
103
-
104
- /** Convert WeChat message to internal Message */
105
- private wechatToMessage(wxMsg: WeChatMessage): Message {
106
- return {
107
- id: wxMsg.msgId ?? `wx-${wxMsg.createTime}`,
108
- role: 'user',
109
- content: wxMsg.content ?? '',
110
- timestamp: wxMsg.createTime * 1000,
111
- metadata: {
112
- channel: 'wechat',
113
- fromUser: wxMsg.fromUserName,
114
- toUser: wxMsg.toUserName,
115
- msgType: wxMsg.msgType,
116
- },
117
- };
118
- }
119
-
120
- /** Send template message */
121
- async sendTemplateMessage(data: TemplateMessageData): Promise<boolean> {
122
- // TODO: Implement
123
- // const token = await this.getAccessToken();
124
- // const url = `https://api.weixin.qq.com/cgi-bin/message/template/send?access_token=${token}`;
125
- // const res = await fetch(url, {
126
- // method: 'POST',
127
- // body: JSON.stringify({
128
- // touser: data.toUser,
129
- // template_id: data.templateId,
130
- // url: data.url,
131
- // data: data.data,
132
- // }),
133
- // });
134
- void data;
135
- return true;
136
- }
137
-
138
- /** Generate QR code for login (stub) */
139
- async generateLoginQR(): Promise<{ ticket: string; url: string; expireSeconds: number }> {
140
- // TODO: Implement with WeChat QR code API
141
- // const token = await this.getAccessToken();
142
- // POST to https://api.weixin.qq.com/cgi-bin/qrcode/create
143
- return {
144
- ticket: 'stub-ticket',
145
- url: 'https://mp.weixin.qq.com/cgi-bin/showqrcode?ticket=stub-ticket',
146
- expireSeconds: 300,
147
- };
148
- }
149
- }
1
+ import { BaseChannel } from './index';
2
+ import type { Message } from '../core/types';
3
+
4
+ /**
5
+ * WeChat Channel (Stub) — v0.8.0
6
+ * WeChat Official Account message handling, template messages, QR code login.
7
+ */
8
+
9
+ export interface WeChatChannelConfig {
10
+ /** WeChat Official Account AppID */
11
+ appId: string;
12
+ /** WeChat Official Account AppSecret */
13
+ appSecret: string;
14
+ /** Verification token for message validation */
15
+ token: string;
16
+ /** AES encoding key for encrypted messages */
17
+ encodingAESKey?: string;
18
+ /** HTTP server port (default: 3002) */
19
+ port?: number;
20
+ }
21
+
22
+ export interface WeChatMessage {
23
+ toUserName: string;
24
+ fromUserName: string;
25
+ createTime: number;
26
+ msgType: 'text' | 'image' | 'voice' | 'video' | 'event';
27
+ content?: string;
28
+ msgId?: string;
29
+ event?: string;
30
+ eventKey?: string;
31
+ }
32
+
33
+ export interface TemplateMessageData {
34
+ toUser: string;
35
+ templateId: string;
36
+ url?: string;
37
+ data: Record<string, { value: string; color?: string }>;
38
+ }
39
+
40
+ export class WeChatChannel extends BaseChannel {
41
+ type = 'wechat';
42
+ private config: WeChatChannelConfig;
43
+ private accessToken: string | null = null;
44
+ private tokenExpiry = 0;
45
+
46
+ constructor(config: WeChatChannelConfig) {
47
+ super();
48
+ this.config = config;
49
+ }
50
+
51
+ async start(): Promise<void> {
52
+ // TODO: Start HTTP server to receive WeChat push messages
53
+ // 1. Verify signature on GET requests
54
+ // 2. Parse XML messages on POST requests
55
+ // 3. Route to handler and reply with XML
56
+ }
57
+
58
+ async stop(): Promise<void> {
59
+ // TODO: Stop HTTP server
60
+ }
61
+
62
+ /** Get or refresh access token */
63
+ async getAccessToken(): Promise<string> {
64
+ if (this.accessToken && Date.now() < this.tokenExpiry) {
65
+ return this.accessToken;
66
+ }
67
+
68
+ // TODO: Implement token refresh
69
+ // const url = `https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid=${this.config.appId}&secret=${this.config.appSecret}`;
70
+ // const res = await fetch(url);
71
+ // const data = await res.json();
72
+ // this.accessToken = data.access_token;
73
+ // this.tokenExpiry = Date.now() + (data.expires_in - 300) * 1000;
74
+
75
+ return this.accessToken ?? '';
76
+ }
77
+
78
+ /** Handle incoming WeChat message */
79
+ async handleMessage(wxMsg: WeChatMessage): Promise<string> {
80
+ if (wxMsg.msgType === 'event') {
81
+ return this.handleEvent(wxMsg);
82
+ }
83
+
84
+ const message = this.wechatToMessage(wxMsg);
85
+ if (this.handler) {
86
+ const reply = await this.handler(message);
87
+ return reply.content;
88
+ }
89
+ return '';
90
+ }
91
+
92
+ /** Handle WeChat events (subscribe, scan, etc.) */
93
+ private handleEvent(wxMsg: WeChatMessage): string {
94
+ switch (wxMsg.event) {
95
+ case 'subscribe':
96
+ return 'Welcome! How can I help you?';
97
+ case 'SCAN':
98
+ return `QR code scanned: ${wxMsg.eventKey}`;
99
+ default:
100
+ return '';
101
+ }
102
+ }
103
+
104
+ /** Convert WeChat message to internal Message */
105
+ private wechatToMessage(wxMsg: WeChatMessage): Message {
106
+ return {
107
+ id: wxMsg.msgId ?? `wx-${wxMsg.createTime}`,
108
+ role: 'user',
109
+ content: wxMsg.content ?? '',
110
+ timestamp: wxMsg.createTime * 1000,
111
+ metadata: {
112
+ channel: 'wechat',
113
+ fromUser: wxMsg.fromUserName,
114
+ toUser: wxMsg.toUserName,
115
+ msgType: wxMsg.msgType,
116
+ },
117
+ };
118
+ }
119
+
120
+ /** Send template message */
121
+ async sendTemplateMessage(data: TemplateMessageData): Promise<boolean> {
122
+ // TODO: Implement
123
+ // const token = await this.getAccessToken();
124
+ // const url = `https://api.weixin.qq.com/cgi-bin/message/template/send?access_token=${token}`;
125
+ // const res = await fetch(url, {
126
+ // method: 'POST',
127
+ // body: JSON.stringify({
128
+ // touser: data.toUser,
129
+ // template_id: data.templateId,
130
+ // url: data.url,
131
+ // data: data.data,
132
+ // }),
133
+ // });
134
+ void data;
135
+ return true;
136
+ }
137
+
138
+ /** Generate QR code for login (stub) */
139
+ async generateLoginQR(): Promise<{ ticket: string; url: string; expireSeconds: number }> {
140
+ // TODO: Implement with WeChat QR code API
141
+ // const token = await this.getAccessToken();
142
+ // POST to https://api.weixin.qq.com/cgi-bin/qrcode/create
143
+ return {
144
+ ticket: 'stub-ticket',
145
+ url: 'https://mp.weixin.qq.com/cgi-bin/showqrcode?ticket=stub-ticket',
146
+ expireSeconds: 300,
147
+ };
148
+ }
149
+ }