adp-openclaw 0.0.46 → 0.0.48
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.
- package/index.ts +15 -2
- package/package.json +1 -1
- package/src/adp-upload-tool.ts +9 -0
- package/src/channel.ts +39 -2
- package/src/monitor.ts +51 -5
- package/src/runtime.ts +12 -0
package/index.ts
CHANGED
|
@@ -7,6 +7,8 @@ import {
|
|
|
7
7
|
ADP_UPLOAD_TOOL_SCHEMA,
|
|
8
8
|
parseAdpUploadToolParams,
|
|
9
9
|
uploadFilesToAdpEndpoint,
|
|
10
|
+
uploadResultEmitter,
|
|
11
|
+
UPLOAD_RESULT_EVENT,
|
|
10
12
|
type AdpUploadToolResult,
|
|
11
13
|
type UploadedFileInfo,
|
|
12
14
|
} from "./src/adp-upload-tool.js";
|
|
@@ -184,6 +186,12 @@ const plugin = {
|
|
|
184
186
|
api.logger.info?.(`[${ADP_UPLOAD_TOOL_NAME}] file.downloadUrl: ${file.downloadUrl}`);
|
|
185
187
|
api.logger.info?.(`[${ADP_UPLOAD_TOOL_NAME}] file.uri: ${file.uri}`);
|
|
186
188
|
}
|
|
189
|
+
|
|
190
|
+
// 发射上传结果事件,让 monitor.ts 能够直接获取完整的下载链接
|
|
191
|
+
uploadResultEmitter.emit(UPLOAD_RESULT_EVENT, {
|
|
192
|
+
toolCallId,
|
|
193
|
+
result: successResult,
|
|
194
|
+
});
|
|
187
195
|
|
|
188
196
|
// Build content with resource links and download URLs
|
|
189
197
|
const content: Array<{ type: string; uri?: string; name?: string; mimeType?: string; text?: string; downloadUrl?: string }> = [];
|
|
@@ -200,15 +208,20 @@ const plugin = {
|
|
|
200
208
|
}
|
|
201
209
|
|
|
202
210
|
// Add a text summary with download URLs for AI to include in response
|
|
211
|
+
// 注意:URL 包含签名参数,必须完整保留,不能截断或修改
|
|
203
212
|
const urlSummary = (successResult.files || [])
|
|
204
|
-
.map((f: UploadedFileInfo) =>
|
|
213
|
+
.map((f: UploadedFileInfo) => {
|
|
214
|
+
const url = f.downloadUrl || f.uri;
|
|
215
|
+
// 把完整 URL 作为代码块,防止 AI 截断或修改
|
|
216
|
+
return `- **${f.name}**: \`${url}\``;
|
|
217
|
+
})
|
|
205
218
|
.join("\n");
|
|
206
219
|
|
|
207
220
|
api.logger.info?.(`[${ADP_UPLOAD_TOOL_NAME}] urlSummary: ${urlSummary}`);
|
|
208
221
|
|
|
209
222
|
content.push({
|
|
210
223
|
type: "text",
|
|
211
|
-
text: `Files uploaded successfully:\n${urlSummary}\n\n⚠️
|
|
224
|
+
text: `Files uploaded successfully:\n${urlSummary}\n\n⚠️ IMPORTANT: The URLs above contain authentication signatures. You MUST copy the ENTIRE URL exactly as shown (including all query parameters after the "?"). Do NOT truncate or modify the URLs in any way. The links are valid for 24 hours.`,
|
|
212
225
|
});
|
|
213
226
|
|
|
214
227
|
return {
|
package/package.json
CHANGED
package/src/adp-upload-tool.ts
CHANGED
|
@@ -12,8 +12,17 @@
|
|
|
12
12
|
import { constants } from "node:fs";
|
|
13
13
|
import { access, readFile, stat } from "node:fs/promises";
|
|
14
14
|
import { basename, extname } from "node:path";
|
|
15
|
+
import { EventEmitter } from "node:events";
|
|
15
16
|
import type { AdpOpenclawChannelConfig } from "./channel.js";
|
|
16
17
|
|
|
18
|
+
// ==================== 上传结果事件 ====================
|
|
19
|
+
|
|
20
|
+
/** 上传结果事件发射器,用于通知其他模块上传完成 */
|
|
21
|
+
export const uploadResultEmitter = new EventEmitter();
|
|
22
|
+
|
|
23
|
+
/** 上传结果事件名称 */
|
|
24
|
+
export const UPLOAD_RESULT_EVENT = "adp-upload-result";
|
|
25
|
+
|
|
17
26
|
// ==================== 类型定义 ====================
|
|
18
27
|
|
|
19
28
|
/** 存储凭证请求参数 */
|
package/src/channel.ts
CHANGED
|
@@ -7,6 +7,7 @@ import {
|
|
|
7
7
|
DEFAULT_ACCOUNT_ID,
|
|
8
8
|
} from "openclaw/plugin-sdk";
|
|
9
9
|
import { adpOpenclawOnboardingAdapter } from "./onboarding.js";
|
|
10
|
+
import { getActiveWebSocket } from "./runtime.js";
|
|
10
11
|
|
|
11
12
|
// Default WebSocket URL for ADP OpenClaw
|
|
12
13
|
const DEFAULT_WS_URL = "wss://wss.lke.cloud.tencent.com/bot/gateway/conn";
|
|
@@ -218,6 +219,42 @@ export const adpOpenclawPlugin: ChannelPlugin<ResolvedAdpOpenclawAccount> = {
|
|
|
218
219
|
});
|
|
219
220
|
},
|
|
220
221
|
},
|
|
221
|
-
//
|
|
222
|
-
|
|
222
|
+
// Outbound message support for the "message" tool
|
|
223
|
+
outbound: {
|
|
224
|
+
send: async ({ text, to, log }) => {
|
|
225
|
+
const ws = getActiveWebSocket();
|
|
226
|
+
if (!ws) {
|
|
227
|
+
log?.error?.("[adp-openclaw] No active WebSocket connection for outbound message");
|
|
228
|
+
return { ok: false, error: "No active WebSocket connection" };
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
// Parse target: expected format is "adp-openclaw:{userId}" or "adp-openclaw:bot"
|
|
232
|
+
// The "to" parameter comes from the message tool with format like "adp-openclaw:user123"
|
|
233
|
+
const targetParts = to.split(":");
|
|
234
|
+
const targetUserId = targetParts.length > 1 ? targetParts.slice(1).join(":") : to;
|
|
235
|
+
|
|
236
|
+
log?.info?.(`[adp-openclaw] Sending outbound message to ${targetUserId}: ${text.slice(0, 50)}...`);
|
|
237
|
+
|
|
238
|
+
try {
|
|
239
|
+
// Generate unique request ID
|
|
240
|
+
const requestId = `outbound-${Date.now()}-${Math.random().toString(36).slice(2, 8)}`;
|
|
241
|
+
|
|
242
|
+
const outMsg = {
|
|
243
|
+
type: "outbound",
|
|
244
|
+
requestId,
|
|
245
|
+
payload: {
|
|
246
|
+
to: targetUserId,
|
|
247
|
+
text: text,
|
|
248
|
+
},
|
|
249
|
+
timestamp: Date.now(),
|
|
250
|
+
};
|
|
251
|
+
|
|
252
|
+
ws.send(JSON.stringify(outMsg));
|
|
253
|
+
return { ok: true };
|
|
254
|
+
} catch (err) {
|
|
255
|
+
log?.error?.(`[adp-openclaw] Failed to send outbound message: ${err}`);
|
|
256
|
+
return { ok: false, error: String(err) };
|
|
257
|
+
}
|
|
258
|
+
},
|
|
259
|
+
},
|
|
223
260
|
};
|
package/src/monitor.ts
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
// Supports: API Token auth, conversation tracking for multi-turn dialogues
|
|
3
3
|
|
|
4
4
|
import type { PluginLogger, ClawdbotConfig } from "openclaw/plugin-sdk";
|
|
5
|
-
import { getAdpOpenclawRuntime } from "./runtime.js";
|
|
5
|
+
import { getAdpOpenclawRuntime, setActiveWebSocket } from "./runtime.js";
|
|
6
6
|
import {
|
|
7
7
|
getChatHistory,
|
|
8
8
|
listSessions,
|
|
@@ -14,6 +14,8 @@ import {
|
|
|
14
14
|
ADP_UPLOAD_TOOL_NAME,
|
|
15
15
|
ADP_UPLOAD_TOOL_SCHEMA,
|
|
16
16
|
executeAdpUploadTool,
|
|
17
|
+
uploadResultEmitter,
|
|
18
|
+
UPLOAD_RESULT_EVENT,
|
|
17
19
|
type AdpUploadToolResult,
|
|
18
20
|
} from "./adp-upload-tool.js";
|
|
19
21
|
import {
|
|
@@ -174,6 +176,9 @@ async function connectAndHandle(params: ConnectParams): Promise<void> {
|
|
|
174
176
|
|
|
175
177
|
ws.on("open", () => {
|
|
176
178
|
log?.info(`[adp-openclaw] WebSocket connected, authenticating...`);
|
|
179
|
+
|
|
180
|
+
// Save active WebSocket for outbound messaging
|
|
181
|
+
setActiveWebSocket(ws);
|
|
177
182
|
|
|
178
183
|
// Send authentication message with signature (includes timestamp for anti-replay)
|
|
179
184
|
const nonce = generateNonce();
|
|
@@ -328,20 +333,58 @@ async function connectAndHandle(params: ConnectParams): Promise<void> {
|
|
|
328
333
|
let lastPartialText = ""; // Track last sent text for delta calculation
|
|
329
334
|
let finalSent = false; // Track if outbound_end has been sent
|
|
330
335
|
const displayName = inMsg.user?.username || inMsg.from;
|
|
336
|
+
|
|
337
|
+
// 收集上传结果,在发送最终回复时追加完整的下载链接
|
|
338
|
+
let pendingUploadResults: AdpUploadToolResult[] = [];
|
|
339
|
+
|
|
340
|
+
// 监听上传结果事件
|
|
341
|
+
const uploadResultHandler = (event: { toolCallId: string; result: AdpUploadToolResult }) => {
|
|
342
|
+
log?.info(`[adp-openclaw] Received upload result event for toolCallId=${event.toolCallId}`);
|
|
343
|
+
if (event.result.ok && event.result.files && event.result.files.length > 0) {
|
|
344
|
+
pendingUploadResults.push(event.result);
|
|
345
|
+
// 打印完整的下载链接
|
|
346
|
+
for (const file of event.result.files) {
|
|
347
|
+
log?.info(`[adp-openclaw] Upload result - file.downloadUrl: ${file.downloadUrl}`);
|
|
348
|
+
}
|
|
349
|
+
}
|
|
350
|
+
};
|
|
351
|
+
uploadResultEmitter.on(UPLOAD_RESULT_EVENT, uploadResultHandler);
|
|
331
352
|
|
|
332
353
|
// Helper function to send outbound_end message
|
|
333
354
|
const sendOutboundEnd = (text: string) => {
|
|
334
355
|
if (finalSent) return; // Prevent duplicate sends
|
|
335
356
|
finalSent = true;
|
|
336
357
|
|
|
358
|
+
// 移除事件监听
|
|
359
|
+
uploadResultEmitter.off(UPLOAD_RESULT_EVENT, uploadResultHandler);
|
|
360
|
+
|
|
361
|
+
// 如果有上传结果,追加完整的下载链接到回复中
|
|
362
|
+
let finalText = text;
|
|
363
|
+
if (pendingUploadResults.length > 0) {
|
|
364
|
+
const uploadLinks: string[] = [];
|
|
365
|
+
for (const result of pendingUploadResults) {
|
|
366
|
+
for (const file of (result.files || [])) {
|
|
367
|
+
if (file.downloadUrl) {
|
|
368
|
+
// 使用完整的下载链接,包括签名参数
|
|
369
|
+
uploadLinks.push(`📎 [${file.name}](${file.downloadUrl})`);
|
|
370
|
+
log?.info(`[adp-openclaw] Appending download link: ${file.downloadUrl.substring(0, 100)}...`);
|
|
371
|
+
}
|
|
372
|
+
}
|
|
373
|
+
}
|
|
374
|
+
if (uploadLinks.length > 0) {
|
|
375
|
+
finalText = `${text}\n\n**文件下载链接(24小时有效):**\n${uploadLinks.join("\n")}`;
|
|
376
|
+
log?.info(`[adp-openclaw] Added ${uploadLinks.length} download links to final response`);
|
|
377
|
+
}
|
|
378
|
+
}
|
|
379
|
+
|
|
337
380
|
if (chunkIndex > 0) {
|
|
338
|
-
log?.info(`[adp-openclaw] Sending outbound_end to ${displayName}: ${
|
|
381
|
+
log?.info(`[adp-openclaw] Sending outbound_end to ${displayName}: ${finalText.slice(0, 50)}... (chunks=${chunkIndex})`);
|
|
339
382
|
const endMsg: WSMessage = {
|
|
340
383
|
type: MsgType.OutboundEnd,
|
|
341
384
|
requestId: generateRequestId(),
|
|
342
385
|
payload: {
|
|
343
386
|
to: inMsg.from,
|
|
344
|
-
text:
|
|
387
|
+
text: finalText,
|
|
345
388
|
conversationId: inMsg.conversationId,
|
|
346
389
|
recordId: inMsg.recordId, // Pass recordId back to server
|
|
347
390
|
streamId: streamId,
|
|
@@ -353,13 +396,13 @@ async function connectAndHandle(params: ConnectParams): Promise<void> {
|
|
|
353
396
|
ws.send(JSON.stringify(endMsg));
|
|
354
397
|
} else {
|
|
355
398
|
// No streaming chunks were sent, send as regular outbound message
|
|
356
|
-
log?.info(`[adp-openclaw] Sending outbound to ${displayName}: ${
|
|
399
|
+
log?.info(`[adp-openclaw] Sending outbound to ${displayName}: ${finalText.slice(0, 50)}...`);
|
|
357
400
|
const outMsg: WSMessage = {
|
|
358
401
|
type: MsgType.Outbound,
|
|
359
402
|
requestId: generateRequestId(),
|
|
360
403
|
payload: {
|
|
361
404
|
to: inMsg.from,
|
|
362
|
-
text:
|
|
405
|
+
text: finalText,
|
|
363
406
|
conversationId: inMsg.conversationId,
|
|
364
407
|
recordId: inMsg.recordId, // Pass recordId back to server
|
|
365
408
|
user: inMsg.user,
|
|
@@ -494,6 +537,7 @@ async function connectAndHandle(params: ConnectParams): Promise<void> {
|
|
|
494
537
|
// SDK may call deliver without kind when streaming ends
|
|
495
538
|
if (kind === "final" || kind === undefined) {
|
|
496
539
|
log?.info(`[adp-openclaw] deliver triggering sendOutboundEnd (kind=${kind})`);
|
|
540
|
+
log?.info(`[adp-openclaw] Final text content: ${text}`);
|
|
497
541
|
sendOutboundEnd(text || lastPartialText);
|
|
498
542
|
}
|
|
499
543
|
},
|
|
@@ -650,6 +694,8 @@ async function connectAndHandle(params: ConnectParams): Promise<void> {
|
|
|
650
694
|
ws.on("close", (code, reason) => {
|
|
651
695
|
if (pingInterval) clearInterval(pingInterval);
|
|
652
696
|
abortSignal?.removeEventListener("abort", abortHandler);
|
|
697
|
+
// Clear active WebSocket when connection closes
|
|
698
|
+
setActiveWebSocket(null);
|
|
653
699
|
log?.info(`[adp-openclaw] WebSocket closed: ${code} ${reason.toString()}`);
|
|
654
700
|
resolve();
|
|
655
701
|
});
|
package/src/runtime.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
// Runtime singleton for adp-openclaw plugin
|
|
2
2
|
import type { PluginRuntime } from "openclaw/plugin-sdk";
|
|
3
|
+
import type { WebSocket } from "ws";
|
|
3
4
|
|
|
4
5
|
let adpOpenclawRuntime: PluginRuntime | null = null;
|
|
5
6
|
|
|
@@ -16,6 +17,17 @@ export type PluginConfig = {
|
|
|
16
17
|
|
|
17
18
|
let pluginConfig: PluginConfig = {};
|
|
18
19
|
|
|
20
|
+
// Active WebSocket connection for outbound messaging
|
|
21
|
+
let activeWebSocket: WebSocket | null = null;
|
|
22
|
+
|
|
23
|
+
export function setActiveWebSocket(ws: WebSocket | null): void {
|
|
24
|
+
activeWebSocket = ws;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
export function getActiveWebSocket(): WebSocket | null {
|
|
28
|
+
return activeWebSocket;
|
|
29
|
+
}
|
|
30
|
+
|
|
19
31
|
export function setAdpOpenclawRuntime(runtime: PluginRuntime): void {
|
|
20
32
|
adpOpenclawRuntime = runtime;
|
|
21
33
|
}
|