@quukk/opencode-clawmessenger 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 (105) hide show
  1. package/README.md +526 -0
  2. package/bin/opencode-clawmessenger +2 -0
  3. package/bin/opencode-clawmessenger-setup +5 -0
  4. package/bin/opencode-clawmessenger.cmd +5 -0
  5. package/dist/cli.d.ts +2 -0
  6. package/dist/cli.d.ts.map +1 -0
  7. package/dist/cli.js +288 -0
  8. package/dist/cli.js.map +1 -0
  9. package/dist/core/auto-register.d.ts +24 -0
  10. package/dist/core/auto-register.d.ts.map +1 -0
  11. package/dist/core/auto-register.js +174 -0
  12. package/dist/core/auto-register.js.map +1 -0
  13. package/dist/core/config.d.ts +68 -0
  14. package/dist/core/config.d.ts.map +1 -0
  15. package/dist/core/config.js +80 -0
  16. package/dist/core/config.js.map +1 -0
  17. package/dist/core/daemon.d.ts +19 -0
  18. package/dist/core/daemon.d.ts.map +1 -0
  19. package/dist/core/daemon.js +77 -0
  20. package/dist/core/daemon.js.map +1 -0
  21. package/dist/core/dedup.d.ts +8 -0
  22. package/dist/core/dedup.d.ts.map +1 -0
  23. package/dist/core/dedup.js +25 -0
  24. package/dist/core/dedup.js.map +1 -0
  25. package/dist/core/hook-manager.d.ts +11 -0
  26. package/dist/core/hook-manager.d.ts.map +1 -0
  27. package/dist/core/hook-manager.js +33 -0
  28. package/dist/core/hook-manager.js.map +1 -0
  29. package/dist/core/logger.d.ts +5 -0
  30. package/dist/core/logger.d.ts.map +1 -0
  31. package/dist/core/logger.js +49 -0
  32. package/dist/core/logger.js.map +1 -0
  33. package/dist/core/mac-address.d.ts +2 -0
  34. package/dist/core/mac-address.d.ts.map +1 -0
  35. package/dist/core/mac-address.js +43 -0
  36. package/dist/core/mac-address.js.map +1 -0
  37. package/dist/core/message-handler.d.ts +64 -0
  38. package/dist/core/message-handler.d.ts.map +1 -0
  39. package/dist/core/message-handler.js +879 -0
  40. package/dist/core/message-handler.js.map +1 -0
  41. package/dist/core/ops-assistant.d.ts +26 -0
  42. package/dist/core/ops-assistant.d.ts.map +1 -0
  43. package/dist/core/ops-assistant.js +270 -0
  44. package/dist/core/ops-assistant.js.map +1 -0
  45. package/dist/core/qr-crypto.d.ts +2 -0
  46. package/dist/core/qr-crypto.d.ts.map +1 -0
  47. package/dist/core/qr-crypto.js +66 -0
  48. package/dist/core/qr-crypto.js.map +1 -0
  49. package/dist/core/session-manager.d.ts +22 -0
  50. package/dist/core/session-manager.d.ts.map +1 -0
  51. package/dist/core/session-manager.js +144 -0
  52. package/dist/core/session-manager.js.map +1 -0
  53. package/dist/core/types.d.ts +78 -0
  54. package/dist/core/types.d.ts.map +1 -0
  55. package/dist/core/types.js +26 -0
  56. package/dist/core/types.js.map +1 -0
  57. package/dist/index.d.ts +13 -0
  58. package/dist/index.d.ts.map +1 -0
  59. package/dist/index.js +12 -0
  60. package/dist/index.js.map +1 -0
  61. package/dist/openclaw/client.d.ts +36 -0
  62. package/dist/openclaw/client.d.ts.map +1 -0
  63. package/dist/openclaw/client.js +494 -0
  64. package/dist/openclaw/client.js.map +1 -0
  65. package/dist/opencode/client.d.ts +35 -0
  66. package/dist/opencode/client.d.ts.map +1 -0
  67. package/dist/opencode/client.js +276 -0
  68. package/dist/opencode/client.js.map +1 -0
  69. package/dist/opencode/event-handler.d.ts +38 -0
  70. package/dist/opencode/event-handler.d.ts.map +1 -0
  71. package/dist/opencode/event-handler.js +467 -0
  72. package/dist/opencode/event-handler.js.map +1 -0
  73. package/dist/plugin.d.ts +4 -0
  74. package/dist/plugin.d.ts.map +1 -0
  75. package/dist/plugin.js +84 -0
  76. package/dist/plugin.js.map +1 -0
  77. package/dist/rongcloud/client.d.ts +34 -0
  78. package/dist/rongcloud/client.d.ts.map +1 -0
  79. package/dist/rongcloud/client.js +292 -0
  80. package/dist/rongcloud/client.js.map +1 -0
  81. package/dist/rongcloud/env-polyfill.d.ts +2 -0
  82. package/dist/rongcloud/env-polyfill.d.ts.map +1 -0
  83. package/dist/rongcloud/env-polyfill.js +107 -0
  84. package/dist/rongcloud/env-polyfill.js.map +1 -0
  85. package/dist/rongcloud/server-api.d.ts +38 -0
  86. package/dist/rongcloud/server-api.d.ts.map +1 -0
  87. package/dist/rongcloud/server-api.js +157 -0
  88. package/dist/rongcloud/server-api.js.map +1 -0
  89. package/dist/standalone.d.ts +10 -0
  90. package/dist/standalone.d.ts.map +1 -0
  91. package/dist/standalone.js +229 -0
  92. package/dist/standalone.js.map +1 -0
  93. package/dist/types/plugin.d.ts +20 -0
  94. package/dist/types/plugin.d.ts.map +1 -0
  95. package/dist/types/plugin.js +2 -0
  96. package/dist/types/plugin.js.map +1 -0
  97. package/dist/websocket/client.d.ts +20 -0
  98. package/dist/websocket/client.d.ts.map +1 -0
  99. package/dist/websocket/client.js +88 -0
  100. package/dist/websocket/client.js.map +1 -0
  101. package/dist/websocket/server-client.d.ts +22 -0
  102. package/dist/websocket/server-client.d.ts.map +1 -0
  103. package/dist/websocket/server-client.js +98 -0
  104. package/dist/websocket/server-client.js.map +1 -0
  105. package/package.json +71 -0
@@ -0,0 +1,10 @@
1
+ export interface StartStandaloneOptions {
2
+ configPath?: string;
3
+ autoServe?: boolean;
4
+ }
5
+ export declare function generateBindQR(nodeName?: string): Promise<{
6
+ nodeId: string;
7
+ qrData: string;
8
+ }>;
9
+ export declare function startStandalone(options?: StartStandaloneOptions): Promise<void>;
10
+ //# sourceMappingURL=standalone.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"standalone.d.ts","sourceRoot":"","sources":["../src/standalone.ts"],"names":[],"mappings":"AAeA,MAAM,WAAW,sBAAsB;IACrC,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,SAAS,CAAC,EAAE,OAAO,CAAC;CACrB;AAED,wBAAsB,cAAc,CAAC,QAAQ,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC;IAAE,MAAM,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,CAAC,CAWnG;AAED,wBAAsB,eAAe,CAAC,OAAO,GAAE,sBAA2B,GAAG,OAAO,CAAC,IAAI,CAAC,CAuLzF"}
@@ -0,0 +1,229 @@
1
+ import { ConfigManager } from './core/config.js';
2
+ import { SessionManager } from './core/session-manager.js';
3
+ import { MessageHandler } from './core/message-handler.js';
4
+ import { RongCloudClient } from './rongcloud/client.js';
5
+ import { OpenCodeClient, checkOpencodeStatus } from './opencode/client.js';
6
+ import { EventHandler } from './opencode/event-handler.js';
7
+ import { createLogger } from './core/logger.js';
8
+ import { startStatusWriter } from './core/daemon.js';
9
+ import { getOrRegisterToken, loadAutoConfig, generateNodeId, getAppKey, getAppSecret } from './core/auto-register.js';
10
+ import { encryptQR } from './core/qr-crypto.js';
11
+ import { hostname } from 'os';
12
+ const log = createLogger('standalone');
13
+ export async function generateBindQR(nodeName) {
14
+ let nodeId = (await loadAutoConfig())?.nodeId;
15
+ if (!nodeId) {
16
+ nodeId = generateNodeId();
17
+ }
18
+ const name = nodeName || (await loadAutoConfig())?.nodeName || hostname();
19
+ const bindData = JSON.stringify({ type: 'bind_openclaw', node_id: nodeId, name, timestamp: Date.now() });
20
+ const encrypted = encryptQR(bindData);
21
+ return { nodeId, qrData: encrypted };
22
+ }
23
+ export async function startStandalone(options = {}) {
24
+ const { configPath, autoServe = false } = options;
25
+ console.log('Starting OpenCode ClawMessenger Plugin (Standalone Mode)\n');
26
+ console.log(' - RongCloud IM bridge to OpenCode');
27
+ console.log(' - Auto-registration & heartbeat');
28
+ console.log(' - Message deduplication & session persistence\n');
29
+ const configManager = new ConfigManager(configPath);
30
+ let config;
31
+ try {
32
+ config = configManager.load();
33
+ }
34
+ catch {
35
+ config = configManager.load();
36
+ }
37
+ if (!config.token) {
38
+ log.info('Token missing, auto-registering...');
39
+ const token = await getOrRegisterToken(config.serverUrl, undefined, log);
40
+ if (!token) {
41
+ console.error('自动注册失败,请运行: npx opencode-clawmessenger setup');
42
+ process.exit(1);
43
+ }
44
+ config.token = token;
45
+ config.accountId = (await loadAutoConfig())?.nodeId || generateNodeId();
46
+ log.info('Auto-registration successful');
47
+ }
48
+ console.log(` Account: ${config.accountId}`);
49
+ console.log(` OpenCode: ${config.opencodeUrl}`);
50
+ console.log(` Server: ${config.serverUrl}\n`);
51
+ const serverRunning = await checkOpencodeStatus(config.opencodeUrl, config.opencodePassword);
52
+ if (!serverRunning && autoServe) {
53
+ log.warn('OpenCode server not running; --serve auto-start is not yet implemented for clawmessenger');
54
+ }
55
+ if (!serverRunning) {
56
+ const port = new URL(config.opencodeUrl).port || '19876';
57
+ console.warn(`OpenCode server not running at ${config.opencodeUrl}`);
58
+ console.warn(` Start it: opencode serve --port ${port}\n`);
59
+ }
60
+ // 参考 opencode-feishu:directory 指向包含 .opencode/prompt.md 的目录
61
+ // OpenCode 会自动加载该目录下的 prompt.md 作为 system prompt
62
+ // 优先级:环境变量 CLAW_OPENCODE_DIR > 配置文件的 opencodeDir > process.cwd()
63
+ const opencodeDir = config.opencodeDir || process.cwd();
64
+ const opencode = new OpenCodeClient({
65
+ baseUrl: config.opencodeUrl,
66
+ directory: opencodeDir,
67
+ password: config.opencodePassword,
68
+ });
69
+ try {
70
+ await opencode.listSessions();
71
+ console.log('OpenCode connected\n');
72
+ }
73
+ catch (err) {
74
+ log.error({ err, opencodeUrl: config.opencodeUrl }, 'Failed to connect to OpenCode');
75
+ const port = new URL(config.opencodeUrl).port || '19876';
76
+ console.error(`Failed to connect to OpenCode at ${config.opencodeUrl}`);
77
+ console.error(` Start it: opencode serve --port ${port}\n`);
78
+ process.exit(1);
79
+ }
80
+ const sessionManager = new SessionManager(opencode);
81
+ // 从服务端获取最新的 AppKey
82
+ const fetchedAppKey = await getAppKey(config.serverUrl);
83
+ if (fetchedAppKey !== config.appKey) {
84
+ log?.info(`从服务端获取到新的 AppKey: ${fetchedAppKey}`);
85
+ config.appKey = fetchedAppKey;
86
+ }
87
+ // 从服务端获取 AppSecret(用于流式消息)
88
+ const fetchedAppSecret = await getAppSecret(config.serverUrl, config.token, config.accountId);
89
+ if (fetchedAppSecret) {
90
+ log?.info('从服务端获取到 AppSecret,流式消息已启用');
91
+ config.appSecret = fetchedAppSecret;
92
+ }
93
+ else {
94
+ log?.warn('无法从服务端获取 AppSecret,流式消息将被禁用');
95
+ }
96
+ const rongClient = new RongCloudClient({
97
+ appKey: config.appKey,
98
+ token: config.token,
99
+ accountId: config.accountId,
100
+ }, log);
101
+ const messageHandler = new MessageHandler(config, sessionManager, rongClient, opencode);
102
+ let eventHandler;
103
+ try {
104
+ const eventStream = await opencode.subscribeGlobalEvents();
105
+ eventHandler = new EventHandler(sessionManager, rongClient, opencode, config);
106
+ eventHandler.start(eventStream).catch((err) => {
107
+ log.error({ err }, 'Event stream error');
108
+ });
109
+ console.log('SSE event stream started\n');
110
+ }
111
+ catch (err) {
112
+ log.warn({ err }, 'Failed to start event stream');
113
+ console.warn(' Event streaming disabled\n');
114
+ }
115
+ const connectResult = await rongClient.connect((msg) => {
116
+ messageHandler.handleMessage(msg).catch((err) => {
117
+ log.error({ err }, 'Message handling failed');
118
+ });
119
+ });
120
+ if (!connectResult.success) {
121
+ console.error('Failed to connect to RongCloud');
122
+ process.exit(1);
123
+ }
124
+ // 同步融云 userId 到 config
125
+ if (connectResult.userId) {
126
+ const oldAccountId = config.accountId;
127
+ config.accountId = connectResult.userId;
128
+ log.info({ accountId: config.accountId }, '已同步融云 userId');
129
+ // 如果融云 userId 与之前的 accountId 不同,需要更新服务端
130
+ if (oldAccountId !== connectResult.userId) {
131
+ log.info({ oldAccountId, newAccountId: connectResult.userId }, '融云 userId 已变更,更新服务端');
132
+ await updateNodeRongcloudId(config, oldAccountId, connectResult.userId, log);
133
+ }
134
+ }
135
+ console.log('RongCloud connected\n');
136
+ // 初始注册 claw 节点(只执行一次)
137
+ await registerNodeToServer(config, log);
138
+ // 融云连接检查(每30秒)
139
+ const rongcloudCheckInterval = setInterval(() => {
140
+ if (!rongClient.isConnected) {
141
+ log.warn('RongCloud disconnected, reconnecting...');
142
+ rongClient.connect((msg) => {
143
+ messageHandler.handleMessage(msg).catch((err) => {
144
+ log.error({ err }, 'Message handling failed');
145
+ });
146
+ }).catch((err) => {
147
+ log.error({ err }, 'Reconnect failed');
148
+ });
149
+ }
150
+ }, 30_000);
151
+ console.log('----------------------------------------');
152
+ console.log(' OpenCode ClawMessenger Plugin Running');
153
+ console.log('----------------------------------------');
154
+ console.log(` Mode: Standalone`);
155
+ console.log(` OpenCode: ${config.opencodeUrl}`);
156
+ console.log(` Directory: ${config.opencodeDir || process.cwd()}`);
157
+ console.log('----------------------------------------');
158
+ console.log('Press Ctrl+C to stop');
159
+ const startTime = Date.now();
160
+ const stopStatusWriter = startStatusWriter(() => ({
161
+ startedAt: startTime,
162
+ opencodeUrl: config.opencodeUrl,
163
+ rongcloudConnected: rongClient.isConnected,
164
+ sessionCount: sessionManager.getAllSessions().length,
165
+ }));
166
+ const shutdown = async (signal) => {
167
+ console.log(`\nReceived ${signal}, shutting down...`);
168
+ stopStatusWriter();
169
+ clearInterval(rongcloudCheckInterval);
170
+ if (eventHandler)
171
+ eventHandler.stop();
172
+ rongClient.disconnect();
173
+ await sessionManager.cleanup();
174
+ console.log('Goodbye!');
175
+ process.exit(0);
176
+ };
177
+ process.on('SIGINT', () => shutdown('SIGINT'));
178
+ process.on('SIGTERM', () => shutdown('SIGTERM'));
179
+ setInterval(() => { }, 1000);
180
+ }
181
+ async function registerNodeToServer(config, log) {
182
+ try {
183
+ const { serverUrl, accountId } = config;
184
+ if (!serverUrl || !accountId)
185
+ return;
186
+ // 只注册 claw 节点,获取融云 token
187
+ await fetch(`${serverUrl}/api/register`, {
188
+ method: 'POST',
189
+ headers: { 'Content-Type': 'application/json' },
190
+ body: JSON.stringify({
191
+ node_id: accountId,
192
+ name: config.nodeName || 'OpenCode Agent',
193
+ mac_address: '00:00:00:00:00:00',
194
+ }),
195
+ signal: AbortSignal.timeout(10000),
196
+ });
197
+ log.info('Claw node registered to server');
198
+ }
199
+ catch (error) {
200
+ log.warn({ err: error }, 'Register node to server failed');
201
+ }
202
+ }
203
+ async function updateNodeRongcloudId(config, oldNodeId, newNodeId, log) {
204
+ try {
205
+ const { serverUrl } = config;
206
+ if (!serverUrl)
207
+ return;
208
+ // 调用服务端 API 更新节点的 rongcloud_id
209
+ const response = await fetch(`${serverUrl}/api/claw/update-rongcloud-id`, {
210
+ method: 'POST',
211
+ headers: { 'Content-Type': 'application/json' },
212
+ body: JSON.stringify({
213
+ old_node_id: oldNodeId,
214
+ new_node_id: newNodeId,
215
+ }),
216
+ signal: AbortSignal.timeout(10000),
217
+ });
218
+ if (response.ok) {
219
+ log.info({ oldNodeId, newNodeId }, '服务端节点 rongcloud_id 已更新');
220
+ }
221
+ else {
222
+ log.warn({ status: response.status }, '更新服务端 rongcloud_id 失败');
223
+ }
224
+ }
225
+ catch (error) {
226
+ log.warn({ err: error }, 'Update node rongcloud_id failed');
227
+ }
228
+ }
229
+ //# sourceMappingURL=standalone.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"standalone.js","sourceRoot":"","sources":["../src/standalone.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,20 @@
1
+ export interface PluginInput {
2
+ client: any;
3
+ project: any;
4
+ directory: string;
5
+ worktree: string;
6
+ experimental_workspace: {
7
+ register(type: string, adaptor: any): void;
8
+ };
9
+ serverUrl: URL;
10
+ $: any;
11
+ }
12
+ export interface PluginHooks {
13
+ cleanup?: () => Promise<void>;
14
+ [key: string]: any;
15
+ }
16
+ export interface Plugin {
17
+ id: string;
18
+ server: (input: PluginInput) => Promise<PluginHooks>;
19
+ }
20
+ //# sourceMappingURL=plugin.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"plugin.d.ts","sourceRoot":"","sources":["../../src/types/plugin.ts"],"names":[],"mappings":"AAAA,MAAM,WAAW,WAAW;IAC1B,MAAM,EAAE,GAAG,CAAC;IACZ,OAAO,EAAE,GAAG,CAAC;IACb,SAAS,EAAE,MAAM,CAAC;IAClB,QAAQ,EAAE,MAAM,CAAC;IACjB,sBAAsB,EAAE;QACtB,QAAQ,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,GAAG,GAAG,IAAI,CAAC;KAC5C,CAAC;IACF,SAAS,EAAE,GAAG,CAAC;IACf,CAAC,EAAE,GAAG,CAAC;CACR;AAED,MAAM,WAAW,WAAW;IAC1B,OAAO,CAAC,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;IAC9B,CAAC,GAAG,EAAE,MAAM,GAAG,GAAG,CAAC;CACpB;AAED,MAAM,WAAW,MAAM;IACrB,EAAE,EAAE,MAAM,CAAC;IACX,MAAM,EAAE,CAAC,KAAK,EAAE,WAAW,KAAK,OAAO,CAAC,WAAW,CAAC,CAAC;CACtD"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=plugin.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"plugin.js","sourceRoot":"","sources":["../../src/types/plugin.ts"],"names":[],"mappings":""}
@@ -0,0 +1,20 @@
1
+ import type { Logger } from '../core/logger.js';
2
+ export declare class WebSocketClient {
3
+ private ws;
4
+ private url;
5
+ private nodeId;
6
+ private log;
7
+ private reconnectInterval;
8
+ private heartbeatInterval;
9
+ private reconnectTimer;
10
+ private heartbeatTimer;
11
+ private isConnected;
12
+ constructor(url: string, nodeId: string, log: Logger);
13
+ connect(): void;
14
+ private send;
15
+ private startHeartbeat;
16
+ private stopHeartbeat;
17
+ private scheduleReconnect;
18
+ disconnect(): void;
19
+ }
20
+ //# sourceMappingURL=client.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"client.d.ts","sourceRoot":"","sources":["../../src/websocket/client.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,mBAAmB,CAAC;AAEhD,qBAAa,eAAe;IAC1B,OAAO,CAAC,EAAE,CAA0B;IACpC,OAAO,CAAC,GAAG,CAAS;IACpB,OAAO,CAAC,MAAM,CAAS;IACvB,OAAO,CAAC,GAAG,CAAS;IACpB,OAAO,CAAC,iBAAiB,CAAS;IAClC,OAAO,CAAC,iBAAiB,CAAS;IAClC,OAAO,CAAC,cAAc,CAA+B;IACrD,OAAO,CAAC,cAAc,CAA+B;IACrD,OAAO,CAAC,WAAW,CAAS;gBAEhB,GAAG,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM;IAMpD,OAAO,IAAI,IAAI;IAyCf,OAAO,CAAC,IAAI;IAMZ,OAAO,CAAC,cAAc;IAMtB,OAAO,CAAC,aAAa;IAIrB,OAAO,CAAC,iBAAiB;IAKzB,UAAU,IAAI,IAAI;CAMnB"}
@@ -0,0 +1,88 @@
1
+ import WebSocket from 'ws';
2
+ export class WebSocketClient {
3
+ constructor(url, nodeId, log) {
4
+ this.ws = null;
5
+ this.reconnectInterval = 30000;
6
+ this.heartbeatInterval = 30000;
7
+ this.reconnectTimer = null;
8
+ this.heartbeatTimer = null;
9
+ this.isConnected = false;
10
+ this.url = url;
11
+ this.nodeId = nodeId;
12
+ this.log = log;
13
+ }
14
+ connect() {
15
+ try {
16
+ this.log.info(`Connecting WebSocket: ${this.url}`);
17
+ this.ws = new WebSocket(this.url);
18
+ this.ws.on('open', () => {
19
+ this.isConnected = true;
20
+ this.send({ type: 'register', node_id: this.nodeId, timestamp: new Date().toISOString() });
21
+ this.startHeartbeat();
22
+ });
23
+ this.ws.on('message', (data) => {
24
+ try {
25
+ const message = JSON.parse(data.toString());
26
+ this.log.info({ type: message.type }, 'WebSocket message received');
27
+ // 响应服务端 ping 消息
28
+ if (message.type === 'ping') {
29
+ this.send({ type: 'pong', node_id: this.nodeId, timestamp: new Date().toISOString() });
30
+ }
31
+ }
32
+ catch {
33
+ this.log.warn('Non-JSON WebSocket message');
34
+ }
35
+ });
36
+ this.ws.on('close', () => {
37
+ this.isConnected = false;
38
+ this.stopHeartbeat();
39
+ this.scheduleReconnect();
40
+ });
41
+ this.ws.on('error', (err) => {
42
+ this.log.error({ err }, 'WebSocket error');
43
+ this.isConnected = false;
44
+ });
45
+ }
46
+ catch (err) {
47
+ this.log.error({ err }, 'WebSocket connect failed');
48
+ this.scheduleReconnect();
49
+ }
50
+ }
51
+ send(data) {
52
+ if (this.ws && this.isConnected) {
53
+ try {
54
+ this.ws.send(JSON.stringify(data));
55
+ }
56
+ catch { }
57
+ }
58
+ }
59
+ startHeartbeat() {
60
+ this.heartbeatTimer = setInterval(() => {
61
+ this.send({ type: 'heartbeat', node_id: this.nodeId, timestamp: new Date().toISOString() });
62
+ }, this.heartbeatInterval);
63
+ }
64
+ stopHeartbeat() {
65
+ if (this.heartbeatTimer) {
66
+ clearInterval(this.heartbeatTimer);
67
+ this.heartbeatTimer = null;
68
+ }
69
+ }
70
+ scheduleReconnect() {
71
+ if (this.reconnectTimer)
72
+ clearTimeout(this.reconnectTimer);
73
+ this.reconnectTimer = setTimeout(() => this.connect(), this.reconnectInterval);
74
+ }
75
+ disconnect() {
76
+ this.stopHeartbeat();
77
+ if (this.reconnectTimer) {
78
+ clearTimeout(this.reconnectTimer);
79
+ this.reconnectTimer = null;
80
+ }
81
+ if (this.ws) {
82
+ this.ws.close();
83
+ this.ws = null;
84
+ }
85
+ this.isConnected = false;
86
+ }
87
+ }
88
+ //# sourceMappingURL=client.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"client.js","sourceRoot":"","sources":["../../src/websocket/client.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,22 @@
1
+ import type { Logger } from '../core/logger.js';
2
+ export declare class ServerWebSocketClient {
3
+ private ws;
4
+ private url;
5
+ private nodeId;
6
+ private nodeName;
7
+ private opencodeUrl;
8
+ private log;
9
+ private reconnectInterval;
10
+ private heartbeatInterval;
11
+ private reconnectTimer;
12
+ private heartbeatTimer;
13
+ private isConnected;
14
+ constructor(url: string, nodeId: string, nodeName: string, opencodeUrl: string, log: Logger);
15
+ connect(): void;
16
+ private send;
17
+ private startHeartbeat;
18
+ private stopHeartbeat;
19
+ private scheduleReconnect;
20
+ disconnect(): void;
21
+ }
22
+ //# sourceMappingURL=server-client.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"server-client.d.ts","sourceRoot":"","sources":["../../src/websocket/server-client.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,mBAAmB,CAAC;AAEhD,qBAAa,qBAAqB;IAChC,OAAO,CAAC,EAAE,CAA0B;IACpC,OAAO,CAAC,GAAG,CAAS;IACpB,OAAO,CAAC,MAAM,CAAS;IACvB,OAAO,CAAC,QAAQ,CAAS;IACzB,OAAO,CAAC,WAAW,CAAS;IAC5B,OAAO,CAAC,GAAG,CAAS;IACpB,OAAO,CAAC,iBAAiB,CAAS;IAClC,OAAO,CAAC,iBAAiB,CAAS;IAClC,OAAO,CAAC,cAAc,CAA+B;IACrD,OAAO,CAAC,cAAc,CAA+B;IACrD,OAAO,CAAC,WAAW,CAAS;gBAEhB,GAAG,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,EAAE,WAAW,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM;IAQ3F,OAAO,IAAI,IAAI;IAiDf,OAAO,CAAC,IAAI;IAMZ,OAAO,CAAC,cAAc;IAMtB,OAAO,CAAC,aAAa;IAIrB,OAAO,CAAC,iBAAiB;IAKzB,UAAU,IAAI,IAAI;CAMnB"}
@@ -0,0 +1,98 @@
1
+ import WebSocket from 'ws';
2
+ export class ServerWebSocketClient {
3
+ constructor(url, nodeId, nodeName, opencodeUrl, log) {
4
+ this.ws = null;
5
+ this.reconnectInterval = 30000;
6
+ this.heartbeatInterval = 30000;
7
+ this.reconnectTimer = null;
8
+ this.heartbeatTimer = null;
9
+ this.isConnected = false;
10
+ this.url = url;
11
+ this.nodeId = nodeId;
12
+ this.nodeName = nodeName;
13
+ this.opencodeUrl = opencodeUrl;
14
+ this.log = log;
15
+ }
16
+ connect() {
17
+ try {
18
+ this.log.info({ url: this.url }, 'Connecting server WebSocket');
19
+ this.ws = new WebSocket(this.url);
20
+ this.ws.on('open', () => {
21
+ this.isConnected = true;
22
+ this.send({
23
+ type: 'register',
24
+ node_info: {
25
+ node_id: this.nodeId,
26
+ hostname: this.nodeName,
27
+ local_url: this.opencodeUrl,
28
+ platform: process.platform,
29
+ },
30
+ });
31
+ this.startHeartbeat();
32
+ });
33
+ this.ws.on('message', (data) => {
34
+ try {
35
+ const message = JSON.parse(data.toString());
36
+ this.log.info({ type: message.type }, 'Server message received');
37
+ // 响应服务端 ping 消息
38
+ if (message.type === 'ping') {
39
+ this.send({ type: 'pong', node_id: this.nodeId, timestamp: new Date().toISOString() });
40
+ }
41
+ }
42
+ catch {
43
+ this.log.warn('Non-JSON server message');
44
+ }
45
+ });
46
+ this.ws.on('close', () => {
47
+ this.isConnected = false;
48
+ this.stopHeartbeat();
49
+ this.scheduleReconnect();
50
+ });
51
+ this.ws.on('error', (err) => {
52
+ this.log.error({ err }, 'Server WebSocket error');
53
+ this.isConnected = false;
54
+ });
55
+ }
56
+ catch (err) {
57
+ this.log.error({ err }, 'Server WebSocket connect failed');
58
+ this.scheduleReconnect();
59
+ }
60
+ }
61
+ send(data) {
62
+ if (this.ws && this.isConnected) {
63
+ try {
64
+ this.ws.send(JSON.stringify(data));
65
+ }
66
+ catch { }
67
+ }
68
+ }
69
+ startHeartbeat() {
70
+ this.heartbeatTimer = setInterval(() => {
71
+ this.send({ type: 'heartbeat', node_id: this.nodeId, timestamp: new Date().toISOString() });
72
+ }, this.heartbeatInterval);
73
+ }
74
+ stopHeartbeat() {
75
+ if (this.heartbeatTimer) {
76
+ clearInterval(this.heartbeatTimer);
77
+ this.heartbeatTimer = null;
78
+ }
79
+ }
80
+ scheduleReconnect() {
81
+ if (this.reconnectTimer)
82
+ clearTimeout(this.reconnectTimer);
83
+ this.reconnectTimer = setTimeout(() => this.connect(), this.reconnectInterval);
84
+ }
85
+ disconnect() {
86
+ this.stopHeartbeat();
87
+ if (this.reconnectTimer) {
88
+ clearTimeout(this.reconnectTimer);
89
+ this.reconnectTimer = null;
90
+ }
91
+ if (this.ws) {
92
+ this.ws.close();
93
+ this.ws = null;
94
+ }
95
+ this.isConnected = false;
96
+ }
97
+ }
98
+ //# sourceMappingURL=server-client.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"server-client.js","sourceRoot":"","sources":["../../src/websocket/server-client.ts"],"names":[],"mappings":"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"}
package/package.json ADDED
@@ -0,0 +1,71 @@
1
+ {
2
+ "name": "@quukk/opencode-clawmessenger",
3
+ "version": "0.1.0",
4
+ "description": "OpenCode ClawMessenger Bridge - 融云虾说桥接插件",
5
+ "type": "module",
6
+ "main": "dist/index.js",
7
+ "types": "dist/index.d.ts",
8
+ "publishConfig": {
9
+ "access": "public"
10
+ },
11
+ "bin": {
12
+ "opencode-clawmessenger": "bin/opencode-clawmessenger",
13
+ "opencode-clawmessenger-setup": "bin/opencode-clawmessenger-setup"
14
+ },
15
+ "scripts": {
16
+ "build": "tsc",
17
+ "dev": "tsc --watch",
18
+ "start": "node dist/cli.js",
19
+ "start:win": "chcp 65001 >nul & node dist/cli.js start",
20
+ "lint": "tsc --noEmit",
21
+ "typecheck": "tsc --noEmit",
22
+ "prepublishOnly": "npm run build",
23
+ "release": "semantic-release"
24
+ },
25
+ "keywords": [
26
+ "opencode",
27
+ "clawmessenger",
28
+ "rongcloud",
29
+ "im",
30
+ "bridge"
31
+ ],
32
+ "author": "neomei",
33
+ "license": "MIT",
34
+ "files": [
35
+ "dist",
36
+ "bin",
37
+ "package.json",
38
+ "README.md"
39
+ ],
40
+ "dependencies": {
41
+ "@opencode-ai/sdk": "^1.0.0",
42
+ "@rongcloud/imlib-next": "5.36.6",
43
+ "axios": "^1.15.0",
44
+ "commander": "^12.0.0",
45
+ "fake-indexeddb": "^6.2.5",
46
+ "jsdom": "^24.0.0",
47
+ "pino": "^9.0.0",
48
+ "pino-pretty": "^11.0.0",
49
+ "qrcode": "^1.5.4",
50
+ "ws": "^8.16.0",
51
+ "zod": "^3.23.0"
52
+ },
53
+ "devDependencies": {
54
+ "@semantic-release/changelog": "^6.0.3",
55
+ "@semantic-release/commit-analyzer": "^13.0.1",
56
+ "@semantic-release/git": "^10.0.1",
57
+ "@semantic-release/github": "^12.0.8",
58
+ "@semantic-release/npm": "^13.1.5",
59
+ "@semantic-release/release-notes-generator": "^14.1.1",
60
+ "@types/jsdom": "^21.1.6",
61
+ "@types/node": "^20.11.0",
62
+ "@types/qrcode": "^1.5.6",
63
+ "@types/ws": "^8.5.10",
64
+ "conventional-changelog-conventionalcommits": "^9.3.1",
65
+ "semantic-release": "^25.0.5",
66
+ "typescript": "^5.3.3"
67
+ },
68
+ "engines": {
69
+ "node": ">=18.0.0"
70
+ }
71
+ }