claw_messenger 1.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 (45) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +577 -0
  3. package/bin/auto-init.js +104 -0
  4. package/bin/cli.js +5 -0
  5. package/bin/diagnose-plugin.js +174 -0
  6. package/bin/dm-bridge.cjs +12 -0
  7. package/bin/install.js +452 -0
  8. package/bin/postinstall.js +23 -0
  9. package/bin/qr-crypto-node.js +186 -0
  10. package/bin/setup.js +262 -0
  11. package/dist/auto-register.d.ts +49 -0
  12. package/dist/auto-register.js +328 -0
  13. package/dist/bridge-runner.d.ts +1 -0
  14. package/dist/bridge-runner.js +107 -0
  15. package/dist/cli.d.ts +2 -0
  16. package/dist/cli.js +164 -0
  17. package/dist/device-status.d.ts +30 -0
  18. package/dist/device-status.js +109 -0
  19. package/dist/env-polyfill.d.ts +3 -0
  20. package/dist/env-polyfill.js +166 -0
  21. package/dist/group-config-manager.d.ts +22 -0
  22. package/dist/group-config-manager.js +130 -0
  23. package/dist/index.d.ts +17 -0
  24. package/dist/index.js +36 -0
  25. package/dist/logger.d.ts +14 -0
  26. package/dist/logger.js +103 -0
  27. package/dist/mac-address.d.ts +1 -0
  28. package/dist/mac-address.js +46 -0
  29. package/dist/openclaw-client.d.ts +41 -0
  30. package/dist/openclaw-client.js +530 -0
  31. package/dist/openclaw-config.d.ts +41 -0
  32. package/dist/openclaw-config.js +359 -0
  33. package/dist/openclaw.plugin.json +40 -0
  34. package/dist/package.json +112 -0
  35. package/dist/plugin-entry.d.ts +54 -0
  36. package/dist/plugin-entry.js +772 -0
  37. package/dist/postinstall.js +23 -0
  38. package/dist/rongcloud-client.d.ts +16 -0
  39. package/dist/rongcloud-client.js +274 -0
  40. package/dist/rongcloud-server-api.d.ts +53 -0
  41. package/dist/rongcloud-server-api.js +221 -0
  42. package/dist/utils.d.ts +9 -0
  43. package/dist/utils.js +97 -0
  44. package/openclaw.plugin.json +40 -0
  45. package/package.json +112 -0
@@ -0,0 +1,41 @@
1
+ export interface OpenClawMessage {
2
+ role: 'system' | 'user' | 'assistant';
3
+ content: string | Array<{
4
+ type: string;
5
+ text?: string;
6
+ image_url?: {
7
+ url: string;
8
+ };
9
+ }>;
10
+ }
11
+ export interface Logger {
12
+ info: (msg: string, ...args: any[]) => void;
13
+ warn: (msg: string, ...args: any[]) => void;
14
+ error: (msg: string, ...args: any[]) => void;
15
+ }
16
+ export declare function getOpenClawEnv(baseEnv?: NodeJS.ProcessEnv): NodeJS.ProcessEnv;
17
+ export declare class OpenClawClient {
18
+ static maxConcurrency: number;
19
+ static runningCount: number;
20
+ static waitQueue: Array<() => void>;
21
+ static conversationHistory: Map<string, OpenClawMessage[]>;
22
+ static maxHistoryRounds: number;
23
+ static maxMessageLength: number;
24
+ static activeRequests: Map<string, AbortController>;
25
+ private gatewayStarting;
26
+ private gatewayStarted;
27
+ private gatewayUrl;
28
+ private log;
29
+ constructor(gatewayUrl?: string, log?: Logger);
30
+ static acquireSlot(): Promise<void>;
31
+ static releaseSlot(): void;
32
+ ensureGatewayRunning(): Promise<boolean>;
33
+ private _getConversationHistory;
34
+ private _addToHistory;
35
+ clearHistory(fromUser: string): void;
36
+ private _buildMessagesWithHistory;
37
+ cancelActiveRequest(fromUser: string): boolean;
38
+ chatStream(message: string, fromUser: string, onDelta: (delta: string) => void | Promise<void>, onDone: (fullText: string) => void | Promise<void>, onError?: (error: Error) => void | Promise<void>): Promise<void>;
39
+ private _downloadImageAsBase64;
40
+ private _doChatStream;
41
+ }
@@ -0,0 +1,530 @@
1
+ import { spawn } from 'child_process';
2
+ import net from 'net';
3
+ import os from 'os';
4
+ import fs from 'fs';
5
+ import path from 'path';
6
+ import axios from 'axios';
7
+ import { createLogger } from './logger.js';
8
+ const DEFAULT_GATEWAY_PORT = 18789;
9
+ const defaultLogger = createLogger('openclaw-client');
10
+ function logInfo(log, msg) {
11
+ if (log === null || log === void 0 ? void 0 : log.info) {
12
+ log.info(msg);
13
+ }
14
+ else {
15
+ defaultLogger.info(msg);
16
+ }
17
+ }
18
+ function logWarn(log, msg) {
19
+ if (log === null || log === void 0 ? void 0 : log.warn) {
20
+ log.warn(msg);
21
+ }
22
+ else {
23
+ defaultLogger.warn(msg);
24
+ }
25
+ }
26
+ function logError(log, msg) {
27
+ if (log === null || log === void 0 ? void 0 : log.error) {
28
+ log.error(msg);
29
+ }
30
+ else {
31
+ defaultLogger.error(msg);
32
+ }
33
+ }
34
+ function getRealHomeDir() {
35
+ const envHome = process.env.CLAW_SERVICE_HOME || process.env.USERPROFILE || process.env.HOME;
36
+ if (envHome && !envHome.includes('systemprofile')) {
37
+ return envHome;
38
+ }
39
+ const homeDir = os.homedir();
40
+ if (!homeDir.includes('systemprofile')) {
41
+ return homeDir;
42
+ }
43
+ const usersDir = 'C:\\Users';
44
+ if (fs.existsSync(usersDir)) {
45
+ const entries = fs.readdirSync(usersDir, { withFileTypes: true });
46
+ for (const entry of entries) {
47
+ if (entry.isDirectory() && !['Public', 'Default', 'All Users', 'Default User'].includes(entry.name)) {
48
+ const candidate = path.join(usersDir, entry.name);
49
+ if (fs.existsSync(path.join(candidate, '.openclaw'))) {
50
+ return candidate;
51
+ }
52
+ }
53
+ }
54
+ }
55
+ return homeDir;
56
+ }
57
+ export function getOpenClawEnv(baseEnv = process.env) {
58
+ const realHome = getRealHomeDir();
59
+ const env = Object.assign({}, baseEnv);
60
+ const systemHome = os.homedir();
61
+ env.USERPROFILE = realHome;
62
+ env.HOME = realHome;
63
+ if (process.platform === 'win32') {
64
+ const match = realHome.match(/^([A-Za-z]:)(.*)$/);
65
+ if (match) {
66
+ env.HOMEDRIVE = match[1];
67
+ env.HOMEPATH = match[2];
68
+ }
69
+ const fixPath = (originalPath) => {
70
+ if (!originalPath)
71
+ return null;
72
+ const lowerOriginal = originalPath.toLowerCase();
73
+ const lowerSystemHome = systemHome.toLowerCase();
74
+ if (lowerOriginal.includes(lowerSystemHome)) {
75
+ const idx = lowerOriginal.indexOf(lowerSystemHome);
76
+ return originalPath.substring(0, idx) + realHome + originalPath.substring(idx + systemHome.length);
77
+ }
78
+ return null;
79
+ };
80
+ if (baseEnv.APPDATA) {
81
+ const fixed = fixPath(baseEnv.APPDATA);
82
+ if (fixed)
83
+ env.APPDATA = fixed;
84
+ }
85
+ if (!env.APPDATA) {
86
+ env.APPDATA = path.join(realHome, 'AppData', 'Roaming');
87
+ }
88
+ if (baseEnv.LOCALAPPDATA) {
89
+ const fixed = fixPath(baseEnv.LOCALAPPDATA);
90
+ if (fixed)
91
+ env.LOCALAPPDATA = fixed;
92
+ }
93
+ if (!env.LOCALAPPDATA) {
94
+ env.LOCALAPPDATA = path.join(realHome, 'AppData', 'Local');
95
+ }
96
+ }
97
+ return env;
98
+ }
99
+ function getGatewayToken() {
100
+ var _a, _b;
101
+ const homeDir = getRealHomeDir();
102
+ const possibleFiles = [
103
+ path.join(homeDir, '.openclaw', 'openclaw.json'),
104
+ path.join(homeDir, '.openclaw', 'config.json'),
105
+ path.join(homeDir, '.openclaw', 'tools.json'),
106
+ path.join(homeDir, '.openclaw', 'settings.json'),
107
+ ];
108
+ for (const filePath of possibleFiles) {
109
+ try {
110
+ if (fs.existsSync(filePath)) {
111
+ const content = fs.readFileSync(filePath, 'utf-8');
112
+ const config = JSON.parse(content);
113
+ const token = config.gatewayToken ||
114
+ ((_b = (_a = config.gateway) === null || _a === void 0 ? void 0 : _a.auth) === null || _b === void 0 ? void 0 : _b.token) ||
115
+ config.token ||
116
+ config.apiKey ||
117
+ config.api_key ||
118
+ config.password;
119
+ if (token)
120
+ return String(token);
121
+ }
122
+ }
123
+ catch (_c) {
124
+ // ignore
125
+ }
126
+ }
127
+ return null;
128
+ }
129
+ function checkPort(port) {
130
+ return new Promise((resolve) => {
131
+ const sock = new net.Socket();
132
+ sock.setTimeout(3000);
133
+ sock.once('connect', () => {
134
+ sock.destroy();
135
+ resolve(true);
136
+ });
137
+ sock.once('error', () => {
138
+ sock.destroy();
139
+ resolve(false);
140
+ });
141
+ sock.once('timeout', () => {
142
+ sock.destroy();
143
+ resolve(false);
144
+ });
145
+ sock.connect(port, '127.0.0.1');
146
+ });
147
+ }
148
+ function ensureChatCompletionsConfig(log) {
149
+ const realHome = getRealHomeDir();
150
+ const openclawDir = path.join(realHome, '.openclaw');
151
+ const configPath = path.join(openclawDir, 'openclaw.json');
152
+ let settings = {};
153
+ let existed = false;
154
+ try {
155
+ if (fs.existsSync(configPath)) {
156
+ const content = fs.readFileSync(configPath, 'utf-8');
157
+ settings = JSON.parse(content);
158
+ existed = true;
159
+ }
160
+ }
161
+ catch (_a) {
162
+ settings = {};
163
+ }
164
+ const gateway = settings.gateway || {};
165
+ const http = gateway.http || {};
166
+ const endpoints = http.endpoints || {};
167
+ const chatCompletions = endpoints.chatCompletions || {};
168
+ if (chatCompletions.enabled === true) {
169
+ logInfo(log, 'openclaw.json 中 chatCompletions 已启用');
170
+ return;
171
+ }
172
+ endpoints.chatCompletions = { enabled: true };
173
+ http.endpoints = endpoints;
174
+ gateway.http = http;
175
+ settings.gateway = gateway;
176
+ try {
177
+ if (!fs.existsSync(openclawDir)) {
178
+ fs.mkdirSync(openclawDir, { recursive: true });
179
+ }
180
+ fs.writeFileSync(configPath, JSON.stringify(settings, null, 2), 'utf-8');
181
+ logInfo(log, `已自动在 openclaw.json 中启用 chatCompletions (${existed ? '更新' : '新建'})`);
182
+ }
183
+ catch (err) {
184
+ logWarn(log, `写入 openclaw.json 失败: ${err.message}`);
185
+ }
186
+ }
187
+ function startOpenClawGateway(log) {
188
+ return new Promise((resolve) => {
189
+ ensureChatCompletionsConfig(log);
190
+ logInfo(log, '正在启动 OpenClaw gateway...');
191
+ const child = spawn('openclaw', ['gateway'], {
192
+ shell: true,
193
+ windowsHide: true,
194
+ detached: true,
195
+ stdio: 'ignore',
196
+ env: getOpenClawEnv(),
197
+ });
198
+ child.unref();
199
+ let attempts = 0;
200
+ const maxAttempts = 20;
201
+ const interval = setInterval(async () => {
202
+ attempts++;
203
+ const gatewayRunning = await checkPort(DEFAULT_GATEWAY_PORT);
204
+ if (gatewayRunning) {
205
+ clearInterval(interval);
206
+ logInfo(log, `OpenClaw gateway 启动成功 (${DEFAULT_GATEWAY_PORT})`);
207
+ resolve(true);
208
+ }
209
+ else if (attempts >= maxAttempts) {
210
+ clearInterval(interval);
211
+ logWarn(log, 'OpenClaw gateway 启动超时');
212
+ resolve(false);
213
+ }
214
+ }, 1000);
215
+ child.on('error', (err) => {
216
+ logError(log, `启动 gateway 失败: ${err.message}`);
217
+ clearInterval(interval);
218
+ resolve(false);
219
+ });
220
+ });
221
+ }
222
+ export class OpenClawClient {
223
+ constructor(gatewayUrl = `http://127.0.0.1:${DEFAULT_GATEWAY_PORT}`, log) {
224
+ this.gatewayStarting = false;
225
+ this.gatewayStarted = false;
226
+ this.gatewayUrl = gatewayUrl;
227
+ this.log = log;
228
+ }
229
+ static async acquireSlot() {
230
+ if (OpenClawClient.runningCount < OpenClawClient.maxConcurrency) {
231
+ OpenClawClient.runningCount++;
232
+ return;
233
+ }
234
+ return new Promise((resolve) => OpenClawClient.waitQueue.push(resolve));
235
+ }
236
+ static releaseSlot() {
237
+ OpenClawClient.runningCount--;
238
+ if (OpenClawClient.waitQueue.length > 0) {
239
+ const next = OpenClawClient.waitQueue.shift();
240
+ OpenClawClient.runningCount++;
241
+ next === null || next === void 0 ? void 0 : next();
242
+ }
243
+ }
244
+ async ensureGatewayRunning() {
245
+ const gatewayRunning = await checkPort(DEFAULT_GATEWAY_PORT);
246
+ if (gatewayRunning) {
247
+ this.gatewayStarted = true;
248
+ return true;
249
+ }
250
+ this.gatewayStarted = false;
251
+ if (this.gatewayStarting) {
252
+ for (let i = 0; i < 25; i++) {
253
+ await new Promise((r) => setTimeout(r, 1000));
254
+ if (await checkPort(DEFAULT_GATEWAY_PORT)) {
255
+ this.gatewayStarted = true;
256
+ return true;
257
+ }
258
+ }
259
+ return false;
260
+ }
261
+ this.gatewayStarting = true;
262
+ try {
263
+ const started = await startOpenClawGateway(this.log);
264
+ this.gatewayStarted = started;
265
+ return started;
266
+ }
267
+ finally {
268
+ this.gatewayStarting = false;
269
+ }
270
+ }
271
+ _getConversationHistory(fromUser) {
272
+ return OpenClawClient.conversationHistory.get(fromUser) || [];
273
+ }
274
+ _addToHistory(fromUser, role, content) {
275
+ let history = this._getConversationHistory(fromUser);
276
+ const truncated = content.length > OpenClawClient.maxMessageLength
277
+ ? content.substring(0, OpenClawClient.maxMessageLength) + '...'
278
+ : content;
279
+ history.push({ role, content: truncated });
280
+ const maxMessages = OpenClawClient.maxHistoryRounds * 2;
281
+ if (history.length > maxMessages) {
282
+ history = history.slice(history.length - maxMessages);
283
+ }
284
+ OpenClawClient.conversationHistory.set(fromUser, history);
285
+ }
286
+ clearHistory(fromUser) {
287
+ OpenClawClient.conversationHistory.delete(fromUser);
288
+ }
289
+ _buildMessagesWithHistory(fromUser, currentMessage) {
290
+ const history = this._getConversationHistory(fromUser);
291
+ return [
292
+ {
293
+ role: 'system',
294
+ content: `When you need to search the web for information, you MUST emit status markers on their own lines. These markers will be removed from the final user-facing message, but the UI uses them to show a "searching web" animation and source list:
295
+ [web_search:searching]
296
+ [web_search:url:<URL>]
297
+ [web_search:done]
298
+
299
+ Emit [web_search:searching] when you start searching, [web_search:url:<URL>] for each source you find, and [web_search:done] when you finish. Do not explain these markers.`
300
+ },
301
+ ...history,
302
+ { role: 'user', content: currentMessage }
303
+ ];
304
+ }
305
+ cancelActiveRequest(fromUser) {
306
+ const active = OpenClawClient.activeRequests.get(fromUser);
307
+ if (active) {
308
+ logInfo(this.log, `取消用户活跃请求: ${fromUser}`);
309
+ active.abort();
310
+ OpenClawClient.activeRequests.delete(fromUser);
311
+ return true;
312
+ }
313
+ return false;
314
+ }
315
+ async chatStream(message, fromUser, onDelta, onDone, onError) {
316
+ var _a, _b;
317
+ if (!message || !message.trim()) {
318
+ onError === null || onError === void 0 ? void 0 : onError(new Error('消息内容为空'));
319
+ return;
320
+ }
321
+ const gatewayReady = await this.ensureGatewayRunning();
322
+ if (!gatewayReady) {
323
+ const err = new Error('OpenClaw gateway 启动失败');
324
+ logError(this.log, err.message);
325
+ onError === null || onError === void 0 ? void 0 : onError(err);
326
+ return;
327
+ }
328
+ this.cancelActiveRequest(fromUser);
329
+ logInfo(this.log, `准备 SSE 流式调用 OpenClaw, fromUser=${fromUser}`);
330
+ const gatewayToken = getGatewayToken();
331
+ const sessionId = `clawmessenger-${fromUser}`;
332
+ const messagesWithHistory = this._buildMessagesWithHistory(fromUser, message);
333
+ const endpoints = [
334
+ `${this.gatewayUrl}/v1/chat/completions`,
335
+ `${this.gatewayUrl}/v1/responses`,
336
+ ];
337
+ for (let i = 0; i < endpoints.length; i++) {
338
+ const apiUrl = endpoints[i];
339
+ try {
340
+ const fullText = await this._doChatStream(apiUrl, gatewayToken, sessionId, fromUser, messagesWithHistory, onDelta);
341
+ this._addToHistory(fromUser, 'user', message);
342
+ this._addToHistory(fromUser, 'assistant', fullText);
343
+ await onDone(fullText);
344
+ return;
345
+ }
346
+ catch (err) {
347
+ if (err.name === 'AbortError' || ((_a = err.message) === null || _a === void 0 ? void 0 : _a.includes('aborted'))) {
348
+ logInfo(this.log, `请求被用户取消: ${fromUser}`);
349
+ return;
350
+ }
351
+ const is404 = ((_b = err.response) === null || _b === void 0 ? void 0 : _b.status) === 404;
352
+ const isLast = i === endpoints.length - 1;
353
+ if (is404 && !isLast) {
354
+ logWarn(this.log, `SSE 端点 ${apiUrl} 返回 404,尝试备用端点`);
355
+ continue;
356
+ }
357
+ if (is404 && isLast) {
358
+ logError(this.log, '所有 SSE 端点均返回 404。OpenClaw responses 端点未启用。');
359
+ logError(this.log, '请检查 ~/.openclaw/openclaw.json 中是否包含: gateway.http.endpoints.responses.enabled = true');
360
+ }
361
+ else {
362
+ logError(this.log, `SSE 请求失败: ${err.message}`);
363
+ }
364
+ onError === null || onError === void 0 ? void 0 : onError(err);
365
+ return;
366
+ }
367
+ }
368
+ }
369
+ async _downloadImageAsBase64(imageUrl) {
370
+ const response = await axios.get(imageUrl, {
371
+ responseType: 'arraybuffer',
372
+ timeout: 30000,
373
+ maxContentLength: 10 * 1024 * 1024,
374
+ });
375
+ const buffer = Buffer.from(response.data);
376
+ const base64 = buffer.toString('base64');
377
+ const mediaType = response.headers['content-type'] || 'image/jpeg';
378
+ return { base64, mediaType };
379
+ }
380
+ async _doChatStream(apiUrl, gatewayToken, sessionId, fromUser, messages, onDelta) {
381
+ const headers = {
382
+ 'Content-Type': 'application/json',
383
+ Accept: 'text/event-stream',
384
+ };
385
+ if (gatewayToken) {
386
+ headers.Authorization = `Bearer ${gatewayToken}`;
387
+ }
388
+ const lastMessage = messages[messages.length - 1];
389
+ const messageContent = typeof (lastMessage === null || lastMessage === void 0 ? void 0 : lastMessage.content) === 'string' ? lastMessage.content : '';
390
+ const imageUrlMatch = messageContent.match(/\[图片\]\s*(https?:\/\/[^\s]+)/);
391
+ let payload;
392
+ if (imageUrlMatch) {
393
+ const imageUrl = imageUrlMatch[1];
394
+ const textContent = messageContent.replace(/\[图片\]\s*https?:\/\/[^\s]+/, '').trim();
395
+ try {
396
+ const imageData = await this._downloadImageAsBase64(imageUrl);
397
+ const messagesWithImage = messages.slice(0, -1).concat([{
398
+ role: 'user',
399
+ content: [
400
+ { type: 'text', text: textContent || '' },
401
+ {
402
+ type: 'image_url',
403
+ image_url: { url: `data:${imageData.mediaType};base64,${imageData.base64}` },
404
+ },
405
+ ],
406
+ }]);
407
+ payload = {
408
+ model: 'openclaw',
409
+ messages: messagesWithImage,
410
+ stream: true,
411
+ max_tokens: 2048,
412
+ };
413
+ }
414
+ catch (err) {
415
+ logWarn(this.log, `图片处理失败,回退到文本模式: ${err.message}`);
416
+ payload = {
417
+ model: 'openclaw',
418
+ messages,
419
+ stream: true,
420
+ max_tokens: 2048,
421
+ };
422
+ }
423
+ }
424
+ else {
425
+ payload = {
426
+ model: 'openclaw',
427
+ messages,
428
+ stream: true,
429
+ max_tokens: 2048,
430
+ };
431
+ }
432
+ logInfo(this.log, `SSE 请求: ${apiUrl}, payload=${JSON.stringify(payload).substring(0, 200)}`);
433
+ const abortController = new AbortController();
434
+ OpenClawClient.activeRequests.set(fromUser, abortController);
435
+ const response = await axios.post(apiUrl, payload, {
436
+ headers,
437
+ responseType: 'stream',
438
+ timeout: 600000,
439
+ signal: abortController.signal,
440
+ });
441
+ return new Promise((resolve, reject) => {
442
+ let fullText = '';
443
+ let buffer = '';
444
+ let lastChunkData = null;
445
+ let hasError = false;
446
+ let errorMsg = '';
447
+ response.data.on('data', (chunk) => {
448
+ var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l;
449
+ buffer += chunk.toString();
450
+ const lines = buffer.split('\n');
451
+ buffer = lines.pop() || '';
452
+ for (const line of lines) {
453
+ const trimmed = line.trim();
454
+ if (!trimmed || !trimmed.startsWith('data: '))
455
+ continue;
456
+ const dataStr = trimmed.slice(6).trim();
457
+ if (dataStr === '[DONE]')
458
+ continue;
459
+ try {
460
+ const data = JSON.parse(dataStr);
461
+ lastChunkData = data;
462
+ if (data.error) {
463
+ hasError = true;
464
+ errorMsg = typeof data.error === 'string' ? data.error : ((_a = data.error) === null || _a === void 0 ? void 0 : _a.message) || JSON.stringify(data.error);
465
+ logError(this.log, `SSE chunk error: ${errorMsg}`);
466
+ continue;
467
+ }
468
+ const choice = (_b = data.choices) === null || _b === void 0 ? void 0 : _b[0];
469
+ let delta = null;
470
+ if (choice) {
471
+ const deltaObj = choice.delta;
472
+ delta =
473
+ (_h = (_g = (_f = (_e = (_c = deltaObj === null || deltaObj === void 0 ? void 0 : deltaObj.content) !== null && _c !== void 0 ? _c : (_d = choice.message) === null || _d === void 0 ? void 0 : _d.content) !== null && _e !== void 0 ? _e : choice.text) !== null && _f !== void 0 ? _f : deltaObj === null || deltaObj === void 0 ? void 0 : deltaObj.text) !== null && _g !== void 0 ? _g : deltaObj === null || deltaObj === void 0 ? void 0 : deltaObj.reasoning_content) !== null && _h !== void 0 ? _h : null;
474
+ }
475
+ if (delta === null || delta === undefined) {
476
+ delta = (_l = (_k = (_j = data.content) !== null && _j !== void 0 ? _j : data.delta) !== null && _k !== void 0 ? _k : data.text) !== null && _l !== void 0 ? _l : null;
477
+ }
478
+ if (typeof delta === 'string' && delta.length > 0) {
479
+ fullText += delta;
480
+ Promise.resolve(onDelta(delta)).catch(reject);
481
+ }
482
+ }
483
+ catch (_m) {
484
+ // ignore parse error
485
+ }
486
+ }
487
+ });
488
+ response.data.on('end', async () => {
489
+ var _a;
490
+ OpenClawClient.activeRequests.delete(fromUser);
491
+ logInfo(this.log, `SSE 流结束, 长度=${fullText.length}`);
492
+ if (hasError) {
493
+ reject(new Error(`OpenClaw SSE 错误: ${errorMsg}`));
494
+ return;
495
+ }
496
+ if (fullText.length === 0) {
497
+ const choices = lastChunkData === null || lastChunkData === void 0 ? void 0 : lastChunkData.choices;
498
+ const message = (_a = choices === null || choices === void 0 ? void 0 : choices[0]) === null || _a === void 0 ? void 0 : _a.message;
499
+ const lastContent = message === null || message === void 0 ? void 0 : message.content;
500
+ if (typeof lastContent === 'string' && lastContent.length > 0) {
501
+ fullText = lastContent;
502
+ }
503
+ }
504
+ if (fullText.length === 0) {
505
+ reject(new Error('OpenClaw SSE 返回空内容'));
506
+ return;
507
+ }
508
+ try {
509
+ await onDelta('');
510
+ resolve(fullText);
511
+ }
512
+ catch (err) {
513
+ reject(err);
514
+ }
515
+ });
516
+ response.data.on('error', (err) => {
517
+ OpenClawClient.activeRequests.delete(fromUser);
518
+ logError(this.log, `SSE 流错误: ${err.message}`);
519
+ reject(err);
520
+ });
521
+ });
522
+ }
523
+ }
524
+ OpenClawClient.maxConcurrency = 2;
525
+ OpenClawClient.runningCount = 0;
526
+ OpenClawClient.waitQueue = [];
527
+ OpenClawClient.conversationHistory = new Map();
528
+ OpenClawClient.maxHistoryRounds = 50;
529
+ OpenClawClient.maxMessageLength = 2000;
530
+ OpenClawClient.activeRequests = new Map();
@@ -0,0 +1,41 @@
1
+ interface OpenClawSettings {
2
+ plugins?: {
3
+ allow?: string[];
4
+ entries?: Record<string, {
5
+ enabled?: boolean;
6
+ }>;
7
+ installs?: Record<string, any>;
8
+ load?: {
9
+ paths?: string[];
10
+ };
11
+ };
12
+ [key: string]: any;
13
+ }
14
+ export declare function getOpenClawSettings(settingsPath?: string): Promise<OpenClawSettings | null>;
15
+ export declare function getPluginsAllow(): Promise<string[]>;
16
+ export declare function ensurePluginsAllow(settingsPath?: string): Promise<boolean>;
17
+ /**
18
+ * 确保插件在 plugins.entries 中注册
19
+ * OpenClaw 只有在 entries 中配置了才会加载插件
20
+ */
21
+ export declare function ensurePluginEntry(settingsPath?: string): Promise<boolean>;
22
+ /**
23
+ * 确保 channels.claw_messenger 配置包含 nodeName
24
+ * OpenClaw 的 hasMeaningfulChannelConfig 会忽略 enabled,
25
+ * 只有包含非 enabled 属性时才认为 channel 已配置,从而加载插件。
26
+ */
27
+ export declare function ensureChannelConfig(nodeName?: string, settingsPath?: string): Promise<boolean>;
28
+ /**
29
+ * 确保 gateway HTTP chatCompletions 端点已启用
30
+ * 供 SSE 流式调用使用(OpenClaw 默认禁用该端点)
31
+ */
32
+ export declare function ensureChatCompletionsEnabled(settingsPath?: string): Promise<boolean>;
33
+ /**
34
+ * 确保网关模型支持 image 类型
35
+ * OpenClaw 默认模型 type 只有 "text",插件需要 "image" 支持
36
+ * 遍历 gateway.http.endpoints.chatCompletions.models 中的每个模型,
37
+ * 如果 type 是字符串 "text" 则改为 ["text", "image"],
38
+ * 如果 type 是数组则追加 "image"
39
+ */
40
+ export declare function ensureImageModelSupport(settingsPath?: string): Promise<boolean>;
41
+ export {};