adp-openclaw 0.0.50 → 0.0.52

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 CHANGED
@@ -97,12 +97,21 @@ const plugin = {
97
97
 
98
98
  setAdpOpenclawRuntime(api.runtime);
99
99
 
100
- // Register the ADP file upload tool
101
- // This allows AI to send files to users via ADP storage
102
- console.log(`[adp-openclaw] Registering tool: ${ADP_UPLOAD_TOOL_NAME}`);
103
- api.logger.info?.(`[adp-openclaw] Registering tool: ${ADP_UPLOAD_TOOL_NAME}`);
100
+ // Register the ADP file upload tool as a factory function
101
+ // Using a factory so the tool is only available when the message originates from the ADP channel.
102
+ // This prevents other channels (e.g. DingTalk) with multimodal models from picking up this tool.
103
+ console.log(`[adp-openclaw] Registering tool factory: ${ADP_UPLOAD_TOOL_NAME}`);
104
+ api.logger.info?.(`[adp-openclaw] Registering tool factory: ${ADP_UPLOAD_TOOL_NAME}`);
104
105
 
105
- api.registerTool({
106
+ api.registerTool((ctx: { messageChannel?: string; [key: string]: unknown }) => {
107
+ // Only expose this tool when the message comes from the adp-openclaw channel
108
+ const channel = ctx.messageChannel ?? "";
109
+ if (channel && channel !== "adp-openclaw") {
110
+ api.logger.debug?.(`[adp-openclaw] Skipping ${ADP_UPLOAD_TOOL_NAME} for channel: ${channel}`);
111
+ return null;
112
+ }
113
+
114
+ return {
106
115
  name: ADP_UPLOAD_TOOL_NAME,
107
116
  description:
108
117
  "If you can see this tool description, you are connected to the ADP server. " +
@@ -238,7 +247,8 @@ const plugin = {
238
247
  isError: false,
239
248
  };
240
249
  },
241
- });
250
+ }; // end of tool object
251
+ }); // end of factory function passed to registerTool
242
252
 
243
253
  // Log tool registration success
244
254
  console.log(`[adp-openclaw] Tool ${ADP_UPLOAD_TOOL_NAME} registered successfully`);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "adp-openclaw",
3
- "version": "0.0.50",
3
+ "version": "0.0.52",
4
4
  "description": "ADP-OpenClaw demo channel plugin (Go WebSocket backend)",
5
5
  "type": "module",
6
6
  "dependencies": {
@@ -518,7 +518,7 @@ export const ADP_UPLOAD_TOOL_NAME = "adp_upload_file";
518
518
 
519
519
  const ADP_UPLOAD_TOOL_SCHEMA_TITLE = "ADP file upload";
520
520
  const ADP_UPLOAD_TOOL_SCHEMA_DESCRIPTION =
521
- "Upload local files from this machine to ADP storage. If the call succeeds, the files are uploaded and signed download URLs are returned. The user can access the files via these URLs.";
521
+ "Upload local files from this machine to ADP storage. If the call succeeds, the files are uploaded and signed download URLs are returned. The user can access the files via these URLs. IMPORTANT: When presenting download links to users, format them as clickable Markdown links like [filename](url), do NOT wrap URLs in code blocks or backticks.";
522
522
  const ADP_UPLOAD_TOOL_PATHS_DESCRIPTION =
523
523
  "Local filesystem paths only (1-10 total). Any file type is accepted, including text, images, documents, PDFs, archives, and binary files. Use absolute or workspace-relative local paths that are readable files.";
524
524
 
@@ -659,6 +659,8 @@ export interface UploadedFileInfo {
659
659
  export interface AdpUploadToolResult {
660
660
  ok: boolean;
661
661
  files?: UploadedFileInfo[];
662
+ /** 预格式化的消息,AI 可以直接展示给用户(不要用代码块包装) */
663
+ message?: string;
662
664
  error?: {
663
665
  code: number;
664
666
  message: string;
@@ -769,9 +771,16 @@ export const uploadFilesToAdpEndpoint = async (
769
771
  }
770
772
  );
771
773
 
774
+ // 构建预格式化的消息,告诉 AI 如何展示给用户
775
+ const formattedLinks = uploadResults.map(f =>
776
+ `- [${f.name}](${f.downloadUrl})`
777
+ ).join("\n");
778
+ const message = `Files uploaded successfully. Present these download links to the user as clickable Markdown links (do NOT use code blocks):\n${formattedLinks}`;
779
+
772
780
  return {
773
781
  ok: true,
774
782
  files: uploadResults,
783
+ message,
775
784
  };
776
785
  } catch (error) {
777
786
  return {
package/src/channel.ts CHANGED
@@ -219,42 +219,78 @@ export const adpOpenclawPlugin: ChannelPlugin<ResolvedAdpOpenclawAccount> = {
219
219
  });
220
220
  },
221
221
  },
222
- // Outbound message support for the "message" tool
222
+ // Outbound message support for the "message" tool and cron tasks
223
223
  outbound: {
224
- send: async ({ text, to, log }) => {
224
+ deliveryMode: "direct",
225
+
226
+ // Send text message
227
+ sendText: async (ctx) => {
228
+ const { to, text } = ctx;
225
229
  const ws = getActiveWebSocket();
230
+
226
231
  if (!ws) {
227
- log?.error?.("[adp-openclaw] No active WebSocket connection for outbound message");
228
- return { ok: false, error: "No active WebSocket connection" };
232
+ console.error("[adp-openclaw] No active WebSocket connection for outbound message");
233
+ throw new Error("No active WebSocket connection");
229
234
  }
230
235
 
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"
236
+ // Parse target: expected format is "adp-openclaw:{userId}" or just "{userId}"
233
237
  const targetParts = to.split(":");
234
238
  const targetUserId = targetParts.length > 1 ? targetParts.slice(1).join(":") : to;
235
239
 
236
- log?.info?.(`[adp-openclaw] Sending outbound message to ${targetUserId}: ${text.slice(0, 50)}...`);
240
+ console.log(`[adp-openclaw] Sending outbound text to ${targetUserId}: ${text.slice(0, 50)}...`);
241
+
242
+ // Generate unique request ID
243
+ const requestId = `outbound-${Date.now()}-${Math.random().toString(36).slice(2, 8)}`;
244
+
245
+ const outMsg = {
246
+ type: "outbound",
247
+ requestId,
248
+ payload: {
249
+ to: targetUserId,
250
+ text: text,
251
+ },
252
+ timestamp: Date.now(),
253
+ };
254
+
255
+ ws.send(JSON.stringify(outMsg));
256
+ return { channel: "adp-openclaw", messageId: requestId };
257
+ },
258
+
259
+ // Send media message (text with optional media URL)
260
+ sendMedia: async (ctx) => {
261
+ const { to, text, mediaUrl } = ctx;
262
+ const ws = getActiveWebSocket();
237
263
 
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) };
264
+ if (!ws) {
265
+ console.error("[adp-openclaw] No active WebSocket connection for outbound media");
266
+ throw new Error("No active WebSocket connection");
257
267
  }
268
+
269
+ // Parse target
270
+ const targetParts = to.split(":");
271
+ const targetUserId = targetParts.length > 1 ? targetParts.slice(1).join(":") : to;
272
+
273
+ console.log(`[adp-openclaw] Sending outbound media to ${targetUserId}: ${text.slice(0, 50)}... (media: ${mediaUrl || "none"})`);
274
+
275
+ // Generate unique request ID
276
+ const requestId = `outbound-media-${Date.now()}-${Math.random().toString(36).slice(2, 8)}`;
277
+
278
+ // Combine text and media URL if present
279
+ const finalText = mediaUrl ? `${text}\n\n📎 ${mediaUrl}` : text;
280
+
281
+ const outMsg = {
282
+ type: "outbound",
283
+ requestId,
284
+ payload: {
285
+ to: targetUserId,
286
+ text: finalText,
287
+ mediaUrl: mediaUrl,
288
+ },
289
+ timestamp: Date.now(),
290
+ };
291
+
292
+ ws.send(JSON.stringify(outMsg));
293
+ return { channel: "adp-openclaw", messageId: requestId };
258
294
  },
259
295
  },
260
296
  };