@neomei/opencode-qiwei 0.1.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 (55) hide show
  1. package/README.md +38 -0
  2. package/bin/opencode-qiwei +96 -0
  3. package/dist/core/config.d.ts +62 -0
  4. package/dist/core/config.d.ts.map +1 -0
  5. package/dist/core/config.js +47 -0
  6. package/dist/core/config.js.map +1 -0
  7. package/dist/core/group-policy.d.ts +12 -0
  8. package/dist/core/group-policy.d.ts.map +1 -0
  9. package/dist/core/group-policy.js +26 -0
  10. package/dist/core/group-policy.js.map +1 -0
  11. package/dist/core/logger.d.ts +8 -0
  12. package/dist/core/logger.d.ts.map +1 -0
  13. package/dist/core/logger.js +20 -0
  14. package/dist/core/logger.js.map +1 -0
  15. package/dist/core/media-handler.d.ts +22 -0
  16. package/dist/core/media-handler.d.ts.map +1 -0
  17. package/dist/core/media-handler.js +51 -0
  18. package/dist/core/media-handler.js.map +1 -0
  19. package/dist/core/message-handler.d.ts +38 -0
  20. package/dist/core/message-handler.d.ts.map +1 -0
  21. package/dist/core/message-handler.js +257 -0
  22. package/dist/core/message-handler.js.map +1 -0
  23. package/dist/core/session-manager.d.ts +33 -0
  24. package/dist/core/session-manager.d.ts.map +1 -0
  25. package/dist/core/session-manager.js +75 -0
  26. package/dist/core/session-manager.js.map +1 -0
  27. package/dist/core/template-card-manager.d.ts +32 -0
  28. package/dist/core/template-card-manager.d.ts.map +1 -0
  29. package/dist/core/template-card-manager.js +46 -0
  30. package/dist/core/template-card-manager.js.map +1 -0
  31. package/dist/core/types.d.ts +79 -0
  32. package/dist/core/types.d.ts.map +1 -0
  33. package/dist/core/types.js +2 -0
  34. package/dist/core/types.js.map +1 -0
  35. package/dist/index.d.ts +23 -0
  36. package/dist/index.d.ts.map +1 -0
  37. package/dist/index.js +22 -0
  38. package/dist/index.js.map +1 -0
  39. package/dist/opencode/client.d.ts +27 -0
  40. package/dist/opencode/client.d.ts.map +1 -0
  41. package/dist/opencode/client.js +65 -0
  42. package/dist/opencode/client.js.map +1 -0
  43. package/dist/opencode/event-handler.d.ts +25 -0
  44. package/dist/opencode/event-handler.d.ts.map +1 -0
  45. package/dist/opencode/event-handler.js +167 -0
  46. package/dist/opencode/event-handler.js.map +1 -0
  47. package/dist/plugin.d.ts +14 -0
  48. package/dist/plugin.d.ts.map +1 -0
  49. package/dist/plugin.js +58 -0
  50. package/dist/plugin.js.map +1 -0
  51. package/dist/standalone.d.ts +8 -0
  52. package/dist/standalone.d.ts.map +1 -0
  53. package/dist/standalone.js +84 -0
  54. package/dist/standalone.js.map +1 -0
  55. package/package.json +42 -0
@@ -0,0 +1,27 @@
1
+ /**
2
+ * OpenCode SDK 封装
3
+ * 通过 HTTP API 与 OpenCode serve 通信
4
+ */
5
+ interface OpenCodeClientOptions {
6
+ baseUrl: string;
7
+ }
8
+ export declare class OpenCodeClient {
9
+ private baseUrl;
10
+ constructor(options: OpenCodeClientOptions);
11
+ /** 创建 session */
12
+ createSession(title?: string): Promise<{
13
+ id: string;
14
+ }>;
15
+ /** 发送 prompt */
16
+ sendPrompt(sessionId: string, text: string): Promise<any>;
17
+ /** 列出 sessions */
18
+ listSessions(): Promise<any[]>;
19
+ /** 订阅 SSE 事件 */
20
+ subscribeEvents(): Promise<ReadableStream<Uint8Array>>;
21
+ /** 批准权限 */
22
+ replyPermission(permId: string, reply: 'once' | 'always' | 'reject'): Promise<void>;
23
+ /** 回复问题 */
24
+ replyQuestion(requestID: string, answers: string[][]): Promise<void>;
25
+ }
26
+ export {};
27
+ //# sourceMappingURL=client.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"client.d.ts","sourceRoot":"","sources":["../../src/opencode/client.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAMH,UAAU,qBAAqB;IAC7B,OAAO,EAAE,MAAM,CAAC;CACjB;AAED,qBAAa,cAAc;IACzB,OAAO,CAAC,OAAO,CAAS;gBAEZ,OAAO,EAAE,qBAAqB;IAI1C,iBAAiB;IACX,aAAa,CAAC,KAAK,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC;QAAE,EAAE,EAAE,MAAM,CAAA;KAAE,CAAC;IAS5D,gBAAgB;IACV,UAAU,CAAC,SAAS,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,GAAG,CAAC;IAS/D,kBAAkB;IACZ,YAAY,IAAI,OAAO,CAAC,GAAG,EAAE,CAAC;IAKpC,gBAAgB;IACV,eAAe,IAAI,OAAO,CAAC,cAAc,CAAC,UAAU,CAAC,CAAC;IAQ5D,WAAW;IACL,eAAe,CAAC,MAAM,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,QAAQ,GAAG,QAAQ,GAAG,OAAO,CAAC,IAAI,CAAC;IASzF,WAAW;IACL,aAAa,CAAC,SAAS,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,EAAE,GAAG,OAAO,CAAC,IAAI,CAAC;CAQ3E"}
@@ -0,0 +1,65 @@
1
+ /**
2
+ * OpenCode SDK 封装
3
+ * 通过 HTTP API 与 OpenCode serve 通信
4
+ */
5
+ import { createLogger } from '../core/logger.js';
6
+ const log = createLogger('OpenCodeClient');
7
+ export class OpenCodeClient {
8
+ baseUrl;
9
+ constructor(options) {
10
+ this.baseUrl = options.baseUrl.replace(/\/$/, '');
11
+ }
12
+ /** 创建 session */
13
+ async createSession(title) {
14
+ const res = await fetch(`${this.baseUrl}/session`, {
15
+ method: 'POST',
16
+ headers: { 'Content-Type': 'application/json' },
17
+ body: JSON.stringify({ title: title || '企微会话' }),
18
+ });
19
+ return res.json();
20
+ }
21
+ /** 发送 prompt */
22
+ async sendPrompt(sessionId, text) {
23
+ const res = await fetch(`${this.baseUrl}/session/${sessionId}/message`, {
24
+ method: 'POST',
25
+ headers: { 'Content-Type': 'application/json' },
26
+ body: JSON.stringify({ parts: [{ type: 'text', text }] }),
27
+ });
28
+ return res.json();
29
+ }
30
+ /** 列出 sessions */
31
+ async listSessions() {
32
+ const res = await fetch(`${this.baseUrl}/session`);
33
+ return res.json();
34
+ }
35
+ /** 订阅 SSE 事件 */
36
+ async subscribeEvents() {
37
+ const res = await fetch(`${this.baseUrl}/global/event`, {
38
+ headers: { Accept: 'text/event-stream' },
39
+ });
40
+ if (!res.ok || !res.body)
41
+ throw new Error('SSE connection failed');
42
+ return res.body;
43
+ }
44
+ /** 批准权限 */
45
+ async replyPermission(permId, reply) {
46
+ const res = await fetch(`${this.baseUrl}/permission/${permId}/reply`, {
47
+ method: 'POST',
48
+ headers: { 'Content-Type': 'application/json' },
49
+ body: JSON.stringify({ reply }),
50
+ });
51
+ if (!res.ok)
52
+ throw new Error(`Permission reply failed: ${res.status}`);
53
+ }
54
+ /** 回复问题 */
55
+ async replyQuestion(requestID, answers) {
56
+ const res = await fetch(`${this.baseUrl}/question/${requestID}/reply`, {
57
+ method: 'POST',
58
+ headers: { 'Content-Type': 'application/json' },
59
+ body: JSON.stringify({ answers }),
60
+ });
61
+ if (!res.ok)
62
+ throw new Error(`Question reply failed: ${res.status}`);
63
+ }
64
+ }
65
+ //# sourceMappingURL=client.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"client.js","sourceRoot":"","sources":["../../src/opencode/client.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,25 @@
1
+ /**
2
+ * OpenCode SSE 事件处理器
3
+ *
4
+ * 解析 OpenCode serve 的 SSE 流,桥接到企微:
5
+ * - text delta → MessageHandler.pushStreamDelta()
6
+ * - step-finish → MessageHandler.finishStream()
7
+ * - permission.asked → 通知用户(或自动批准)
8
+ * - question.asked → 通知用户等待回复
9
+ */
10
+ import { SessionManager } from '../core/session-manager.js';
11
+ import { MessageHandler } from '../core/message-handler.js';
12
+ import { OpenCodeClient } from './client.js';
13
+ export declare class OpenCodeEventHandler {
14
+ private sessionManager;
15
+ private messageHandler;
16
+ private opencodeUrl;
17
+ private autoApprove;
18
+ private opencode;
19
+ private isRunning;
20
+ constructor(sessionManager: SessionManager, messageHandler: MessageHandler, opencodeUrl: string, autoApprove: boolean | undefined, opencode: OpenCodeClient);
21
+ start(eventStream: ReadableStream<Uint8Array>): Promise<void>;
22
+ private processEvent;
23
+ stop(): void;
24
+ }
25
+ //# sourceMappingURL=event-handler.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"event-handler.d.ts","sourceRoot":"","sources":["../../src/opencode/event-handler.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,OAAO,EAAE,cAAc,EAAE,MAAM,4BAA4B,CAAC;AAC5D,OAAO,EAAE,cAAc,EAAE,MAAM,4BAA4B,CAAC;AAC5D,OAAO,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;AAM7C,qBAAa,oBAAoB;IAI7B,OAAO,CAAC,cAAc;IACtB,OAAO,CAAC,cAAc;IACtB,OAAO,CAAC,WAAW;IACnB,OAAO,CAAC,WAAW;IACnB,OAAO,CAAC,QAAQ;IAPlB,OAAO,CAAC,SAAS,CAAS;gBAGhB,cAAc,EAAE,cAAc,EAC9B,cAAc,EAAE,cAAc,EAC9B,WAAW,EAAE,MAAM,EACnB,WAAW,EAAE,OAAO,YAAQ,EAC5B,QAAQ,EAAE,cAAc;IAG5B,KAAK,CAAC,WAAW,EAAE,cAAc,CAAC,UAAU,CAAC;YA0BrC,YAAY;IA6H1B,IAAI;CAGL"}
@@ -0,0 +1,167 @@
1
+ /**
2
+ * OpenCode SSE 事件处理器
3
+ *
4
+ * 解析 OpenCode serve 的 SSE 流,桥接到企微:
5
+ * - text delta → MessageHandler.pushStreamDelta()
6
+ * - step-finish → MessageHandler.finishStream()
7
+ * - permission.asked → 通知用户(或自动批准)
8
+ * - question.asked → 通知用户等待回复
9
+ */
10
+ import { createLogger } from '../core/logger.js';
11
+ const log = createLogger('EventHandler');
12
+ export class OpenCodeEventHandler {
13
+ sessionManager;
14
+ messageHandler;
15
+ opencodeUrl;
16
+ autoApprove;
17
+ opencode;
18
+ isRunning = false;
19
+ constructor(sessionManager, messageHandler, opencodeUrl, autoApprove = false, opencode) {
20
+ this.sessionManager = sessionManager;
21
+ this.messageHandler = messageHandler;
22
+ this.opencodeUrl = opencodeUrl;
23
+ this.autoApprove = autoApprove;
24
+ this.opencode = opencode;
25
+ }
26
+ async start(eventStream) {
27
+ this.isRunning = true;
28
+ const reader = eventStream.getReader();
29
+ const decoder = new TextDecoder();
30
+ let buffer = '';
31
+ try {
32
+ while (this.isRunning) {
33
+ const { done, value } = await reader.read();
34
+ if (done)
35
+ break;
36
+ buffer += decoder.decode(value, { stream: true });
37
+ while (buffer.includes('\n\n')) {
38
+ const idx = buffer.indexOf('\n\n');
39
+ const raw = buffer.slice(0, idx);
40
+ buffer = buffer.slice(idx + 2);
41
+ await this.processEvent(raw);
42
+ }
43
+ }
44
+ }
45
+ catch (err) {
46
+ log.error('SSE error', { err });
47
+ }
48
+ }
49
+ async processEvent(raw) {
50
+ let type = '';
51
+ let data = {};
52
+ for (const line of raw.split('\n')) {
53
+ if (line.startsWith('event:'))
54
+ type = line.slice(6).trim();
55
+ if (line.startsWith('data:')) {
56
+ try {
57
+ data = JSON.parse(line.slice(5).trim());
58
+ }
59
+ catch { }
60
+ }
61
+ }
62
+ // 合并顶层字段
63
+ const evt = { ...data, type: type || data.type };
64
+ // 文本增量
65
+ if (evt.type === 'text' || evt.field === 'text') {
66
+ const delta = evt.text || evt.delta || '';
67
+ if (delta) {
68
+ const session = this.sessionManager.getByOpenCodeId(evt.sessionID || '');
69
+ if (session)
70
+ await this.messageHandler.pushStreamDelta(session.id, delta);
71
+ }
72
+ return;
73
+ }
74
+ // 流式结束
75
+ if (evt.type === 'step-finish' || evt.reason === 'stop') {
76
+ const session = this.sessionManager.getByOpenCodeId(evt.sessionID || '');
77
+ if (session)
78
+ await this.messageHandler.finishStream(session.id);
79
+ return;
80
+ }
81
+ // 权限请求
82
+ if (evt.type === 'permission.asked' || evt.type === 'permission.updated') {
83
+ const permId = evt.id || evt.permissionID || '';
84
+ const session = this.sessionManager.getByOpenCodeId(evt.sessionID || '');
85
+ if (this.autoApprove && permId) {
86
+ log.info(`自动批准: ${permId}`);
87
+ this.opencode.replyPermission(permId, 'always').catch(() => { });
88
+ return;
89
+ }
90
+ if (session) {
91
+ const interaction = {
92
+ kind: 'permission',
93
+ data: {
94
+ id: permId,
95
+ permission: evt.permission || evt.type || 'unknown',
96
+ patterns: evt.patterns || (evt.pattern ? [evt.pattern] : []),
97
+ title: evt.title || `${evt.permission}: ${(evt.patterns || []).join(', ')}`,
98
+ },
99
+ };
100
+ this.sessionManager.setPendingInteraction(session.chatId, interaction);
101
+ await this.messageHandler.notifyPending(session.chatId, `🔐 权限请求:${interaction.data.title}\n\n` +
102
+ `请回复「确认」授权一次,或「始终」永久授权,或「拒绝」拒绝该请求。`);
103
+ }
104
+ return;
105
+ }
106
+ // 权限已回复
107
+ if (evt.type === 'permission.replied') {
108
+ const session = this.sessionManager.getByOpenCodeId(evt.sessionID || '');
109
+ if (session) {
110
+ this.sessionManager.clearPendingInteraction(session.chatId);
111
+ }
112
+ return;
113
+ }
114
+ // 问题提问
115
+ if (evt.type === 'question.asked') {
116
+ const session = this.sessionManager.getByOpenCodeId(evt.sessionID || '');
117
+ if (!session)
118
+ return;
119
+ const rawQuestions = evt.questions || [];
120
+ const questions = rawQuestions.map((q) => ({
121
+ question: q.question || '',
122
+ header: q.header || q.question?.substring(0, 30) || '',
123
+ options: (q.options || []).map((o) => ({
124
+ label: o.label || '',
125
+ description: o.description || '',
126
+ })),
127
+ multiple: !!q.multiple,
128
+ custom: q.custom !== false,
129
+ }));
130
+ const interaction = {
131
+ kind: 'question',
132
+ data: {
133
+ id: evt.id || evt.requestID || '',
134
+ questions,
135
+ },
136
+ };
137
+ this.sessionManager.setPendingInteraction(session.chatId, interaction);
138
+ // 构建提示文本
139
+ let prompt = '❓ 需要你提供信息:\n\n';
140
+ for (const [idx, q] of questions.entries()) {
141
+ prompt += `${idx + 1}. ${q.question}\n`;
142
+ if (q.options.length > 0) {
143
+ prompt += '选项:' + q.options.map((o, i) => `${i + 1}.${o.label}`).join(' ') + '\n';
144
+ }
145
+ if (q.custom) {
146
+ prompt += '(也支持直接回复文字)\n';
147
+ }
148
+ prompt += '\n';
149
+ }
150
+ prompt += '请直接回复对应选项编号或内容。';
151
+ await this.messageHandler.notifyPending(session.chatId, prompt);
152
+ return;
153
+ }
154
+ // 问题已回复
155
+ if (evt.type === 'question.replied' || evt.type === 'question.rejected') {
156
+ const session = this.sessionManager.getByOpenCodeId(evt.sessionID || '');
157
+ if (session) {
158
+ this.sessionManager.clearPendingInteraction(session.chatId);
159
+ }
160
+ return;
161
+ }
162
+ }
163
+ stop() {
164
+ this.isRunning = false;
165
+ }
166
+ }
167
+ //# sourceMappingURL=event-handler.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"event-handler.js","sourceRoot":"","sources":["../../src/opencode/event-handler.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,14 @@
1
+ /**
2
+ * opencode-qiwei OpenCode 插件模式
3
+ *
4
+ * 注册为 OpenCode 插件,自动处理企微消息
5
+ * 使用方式:在 ~/.config/opencode/opencode.jsonc 中添加
6
+ * "plugin": ["@neomei/opencode-qiwei"]
7
+ */
8
+ export default function QiweiPlugin(_ctx: any): {
9
+ name: string;
10
+ version: string;
11
+ start(): Promise<void>;
12
+ stop(): Promise<void>;
13
+ };
14
+ //# sourceMappingURL=plugin.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"plugin.d.ts","sourceRoot":"","sources":["../src/plugin.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAYH,MAAM,CAAC,OAAO,UAAU,WAAW,CAAC,IAAI,EAAE,GAAG;;;;;EAkD5C"}
package/dist/plugin.js ADDED
@@ -0,0 +1,58 @@
1
+ /**
2
+ * opencode-qiwei OpenCode 插件模式
3
+ *
4
+ * 注册为 OpenCode 插件,自动处理企微消息
5
+ * 使用方式:在 ~/.config/opencode/opencode.jsonc 中添加
6
+ * "plugin": ["@neomei/opencode-qiwei"]
7
+ */
8
+ import { WSClient } from '@wecom/aibot-node-sdk';
9
+ import { ConfigManager } from './core/config.js';
10
+ import { SessionManager } from './core/session-manager.js';
11
+ import { MessageHandler } from './core/message-handler.js';
12
+ import { OpenCodeClient } from './opencode/client.js';
13
+ import { OpenCodeEventHandler } from './opencode/event-handler.js';
14
+ import { createLogger } from './core/logger.js';
15
+ const log = createLogger('Plugin');
16
+ export default function QiweiPlugin(_ctx) {
17
+ let wsClient;
18
+ let eventHandler;
19
+ let cleanup;
20
+ return {
21
+ name: 'opencode-qiwei',
22
+ version: '0.1.0',
23
+ async start() {
24
+ try {
25
+ const configManager = new ConfigManager();
26
+ const config = configManager.load();
27
+ const opencode = new OpenCodeClient({ baseUrl: config.opencodeUrl });
28
+ const sessionManager = new SessionManager();
29
+ wsClient = new WSClient({
30
+ botId: config.botId,
31
+ secret: config.secret,
32
+ reconnectInterval: 2000,
33
+ maxReconnectAttempts: -1,
34
+ });
35
+ const messageHandler = new MessageHandler(config, sessionManager, wsClient, opencode);
36
+ if (config.streaming) {
37
+ eventHandler = new OpenCodeEventHandler(sessionManager, messageHandler, config.opencodeUrl, config.autoApprove, opencode);
38
+ opencode.subscribeEvents().then(s => eventHandler.start(s)).catch(() => { });
39
+ }
40
+ wsClient.on('message.text', f => messageHandler.handleMessage(f).catch(() => { }));
41
+ wsClient.on('message.image', f => messageHandler.handleImageMessage(f).catch(() => { }));
42
+ wsClient.on('event.enter_chat', f => messageHandler.handleEnterChat(f).catch(() => { }));
43
+ wsClient.on('event.template_card_event', f => messageHandler.handleCardEvent(f).catch(() => { }));
44
+ wsClient.connect();
45
+ log.info('插件已启动');
46
+ cleanup = () => { wsClient?.disconnect(); eventHandler?.stop(); };
47
+ }
48
+ catch (err) {
49
+ log.error('插件启动失败', { err });
50
+ }
51
+ },
52
+ async stop() {
53
+ cleanup?.();
54
+ log.info('插件已停止');
55
+ },
56
+ };
57
+ }
58
+ //# sourceMappingURL=plugin.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"plugin.js","sourceRoot":"","sources":["../src/plugin.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,8 @@
1
+ /**
2
+ * opencode-qiwei 独立运行入口
3
+ * 企微 WSClient → 消息处理 → OpenCode → 流式回复
4
+ */
5
+ export declare function startStandalone(options?: {
6
+ configPath?: string;
7
+ }): Promise<void>;
8
+ //# sourceMappingURL=standalone.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"standalone.d.ts","sourceRoot":"","sources":["../src/standalone.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAaH,wBAAsB,eAAe,CAAC,OAAO,GAAE;IAAE,UAAU,CAAC,EAAE,MAAM,CAAA;CAAO,iBA6E1E"}
@@ -0,0 +1,84 @@
1
+ /**
2
+ * opencode-qiwei 独立运行入口
3
+ * 企微 WSClient → 消息处理 → OpenCode → 流式回复
4
+ */
5
+ import { WSClient } from '@wecom/aibot-node-sdk';
6
+ import { ConfigManager } from './core/config.js';
7
+ import { createLogger } from './core/logger.js';
8
+ import { SessionManager } from './core/session-manager.js';
9
+ import { MessageHandler } from './core/message-handler.js';
10
+ import { OpenCodeClient } from './opencode/client.js';
11
+ import { OpenCodeEventHandler } from './opencode/event-handler.js';
12
+ const log = createLogger('Standalone');
13
+ export async function startStandalone(options = {}) {
14
+ console.log('🚀 启动 OpenCode 企业微信桥接\n');
15
+ // 1. 加载配置
16
+ const configManager = new ConfigManager(options.configPath);
17
+ const config = configManager.load();
18
+ console.log(` Bot ID: ${config.botId}`);
19
+ console.log(` OpenCode: ${config.opencodeUrl}`);
20
+ console.log(` 流式: ${config.streaming ? '✅' : '❌'}`);
21
+ console.log(` 自动授权: ${config.autoApprove ? '✅' : '❌'}\n`);
22
+ // 2. 连接 OpenCode
23
+ const opencode = new OpenCodeClient({ baseUrl: config.opencodeUrl });
24
+ try {
25
+ await opencode.listSessions();
26
+ console.log('✅ OpenCode 已连接\n');
27
+ }
28
+ catch {
29
+ log.warn('OpenCode 未响应');
30
+ }
31
+ // 3. 初始化
32
+ const sessionManager = new SessionManager();
33
+ // 4. 企微 WebSocket
34
+ console.log('📡 连接企业微信 WebSocket...');
35
+ const wsClientOptions = {
36
+ botId: config.botId,
37
+ secret: config.secret,
38
+ reconnectInterval: 2000,
39
+ maxReconnectAttempts: -1,
40
+ heartbeatInterval: 30000,
41
+ };
42
+ // 企业微信后台可能要求 scene 和 plug_version 才能通过认证
43
+ if (config.scene !== undefined) {
44
+ wsClientOptions.scene = config.scene;
45
+ }
46
+ if (config.plugVersion) {
47
+ wsClientOptions.plug_version = config.plugVersion;
48
+ }
49
+ const wsClient = new WSClient(wsClientOptions);
50
+ // 5. 消息处理
51
+ const messageHandler = new MessageHandler(config, sessionManager, wsClient, opencode);
52
+ // 6. SSE 事件 → 流式推送
53
+ let eventHandler;
54
+ if (config.streaming) {
55
+ eventHandler = new OpenCodeEventHandler(sessionManager, messageHandler, config.opencodeUrl, config.autoApprove, opencode);
56
+ // 后台启动 SSE 监听
57
+ opencode.subscribeEvents().then(stream => {
58
+ eventHandler.start(stream).catch(err => log.error('SSE error', { err }));
59
+ }).catch(err => log.warn('SSE 连接失败'));
60
+ }
61
+ // 7. 消息监听
62
+ wsClient.on('message.text', frame => messageHandler.handleMessage(frame).catch(e => log.error('text')));
63
+ wsClient.on('message.image', frame => messageHandler.handleImageMessage(frame).catch(e => log.error('image')));
64
+ wsClient.on('message.mixed', frame => messageHandler.handleMixedMessage(frame).catch(e => log.error('mixed')));
65
+ wsClient.on('event.enter_chat', frame => messageHandler.handleEnterChat(frame).catch(e => log.error('enter')));
66
+ wsClient.on('event.template_card_event', frame => messageHandler.handleCardEvent(frame).catch(e => log.error('card')));
67
+ wsClient.on('connected', () => log.info('🔗 WebSocket 已连接'));
68
+ wsClient.on('authenticated', () => console.log('✅ 企业微信认证成功\n'));
69
+ wsClient.on('disconnected', reason => log.warn(`🔌 断开: ${reason}`));
70
+ wsClient.on('error', err => log.error(`WebSocket 错误: ${err.message}`));
71
+ wsClient.connect();
72
+ // 8. 状态
73
+ console.log('╔════════════════════════════════════════════════╗');
74
+ console.log('║ OpenCode 企业微信桥接 运行中 ║');
75
+ console.log('╠════════════════════════════════════════════════╣');
76
+ console.log(`║ OpenCode: ${config.opencodeUrl.padEnd(36)} ║`);
77
+ console.log(`║ 流式: ${(config.streaming ? 'Markdown 实时推送' : '禁用').padEnd(36)} ║`);
78
+ console.log('╚════════════════════════════════════════════════╝\n');
79
+ // 9. 退出
80
+ const cleanup = () => { wsClient.disconnect(); eventHandler?.stop(); process.exit(0); };
81
+ process.on('SIGINT', cleanup);
82
+ process.on('SIGTERM', cleanup);
83
+ }
84
+ //# sourceMappingURL=standalone.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"standalone.js","sourceRoot":"","sources":["../src/standalone.ts"],"names":[],"mappings":"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"}
package/package.json ADDED
@@ -0,0 +1,42 @@
1
+ {
2
+ "name": "@neomei/opencode-qiwei",
3
+ "version": "0.1.0",
4
+ "description": "OpenCode 企业微信桥接 — 让 OpenCode 接入企业微信",
5
+ "type": "module",
6
+ "main": "dist/index.js",
7
+ "bin": {
8
+ "opencode-qiwei": "bin/opencode-qiwei"
9
+ },
10
+ "files": [
11
+ "dist",
12
+ "bin",
13
+ "README.md"
14
+ ],
15
+ "scripts": {
16
+ "build": "tsc",
17
+ "dev": "tsc --watch",
18
+ "clean": "rm -rf dist",
19
+ "prebuild": "npm run clean",
20
+ "prepublishOnly": "npm run build"
21
+ },
22
+ "keywords": [
23
+ "opencode",
24
+ "plugin",
25
+ "wecom",
26
+ "企业微信",
27
+ "ai"
28
+ ],
29
+ "repository": {
30
+ "type": "git",
31
+ "url": "git+https://github.com/NeoMei/opencode-qiwei.git"
32
+ },
33
+ "license": "MIT",
34
+ "dependencies": {
35
+ "@wecom/aibot-node-sdk": "^1.0.7",
36
+ "zod": "^3.23.0"
37
+ },
38
+ "devDependencies": {
39
+ "@types/node": "^20.19.41",
40
+ "typescript": "^5.9.3"
41
+ }
42
+ }