openclaw-elys 1.8.0 → 1.8.1

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 (2) hide show
  1. package/dist/src/monitor.js +78 -12
  2. package/package.json +1 -1
@@ -1,3 +1,8 @@
1
+ import { createWriteStream } from "node:fs";
2
+ import { mkdtemp } from "node:fs/promises";
3
+ import { tmpdir } from "node:os";
4
+ import { join } from "node:path";
5
+ import { pipeline } from "node:stream/promises";
1
6
  import { loadCredentials } from "./config.js";
2
7
  import { registerDevice } from "./register.js";
3
8
  import { ElysDeviceMQTTClient } from "./mqtt-client.js";
@@ -39,15 +44,25 @@ export async function monitorElysProvider(opts) {
39
44
  try {
40
45
  let seq = 0;
41
46
  let fullText = "";
42
- // Build media context for inbound (user-sent media)
43
- const mediaUrls = cmd.mediaUrls?.length
47
+ // Download inbound media (user-sent) to local temp files
48
+ // OpenClaw expects local file paths in MediaPath/MediaUrl, not remote URLs
49
+ const rawMediaUrls = cmd.mediaUrls?.length
44
50
  ? cmd.mediaUrls
45
51
  : cmd.mediaUrl
46
52
  ? [cmd.mediaUrl]
47
- : undefined;
48
- const mediaTypes = mediaUrls
49
- ? mediaUrls.map(() => cmd.mediaType ?? "application/octet-stream")
50
- : undefined;
53
+ : [];
54
+ const downloadedPaths = [];
55
+ const downloadedTypes = [];
56
+ for (const url of rawMediaUrls) {
57
+ try {
58
+ const localPath = await downloadToTemp(url, log);
59
+ downloadedPaths.push(localPath);
60
+ downloadedTypes.push(cmd.mediaType ?? guessMediaType(url));
61
+ }
62
+ catch (err) {
63
+ log(`[elys] failed to download media ${url}:`, err);
64
+ }
65
+ }
51
66
  const inboundCtx = finalizeCtx({
52
67
  Body: formatCommandAsText(cmd),
53
68
  BodyForAgent: formatCommandAsText(cmd),
@@ -63,12 +78,14 @@ export async function monitorElysProvider(opts) {
63
78
  CommandAuthorized: true,
64
79
  OriginatingChannel: "elys",
65
80
  OriginatingTo: credentials.deviceId,
66
- // Inbound media from user
67
- ...(mediaUrls && {
68
- MediaUrl: mediaUrls[0],
69
- MediaUrls: mediaUrls,
70
- MediaType: mediaTypes[0],
71
- MediaTypes: mediaTypes,
81
+ // Inbound media as local file paths
82
+ ...(downloadedPaths.length > 0 && {
83
+ MediaPath: downloadedPaths[0],
84
+ MediaUrl: downloadedPaths[0],
85
+ MediaPaths: downloadedPaths,
86
+ MediaUrls: downloadedPaths,
87
+ MediaType: downloadedTypes[0],
88
+ MediaTypes: downloadedTypes,
72
89
  }),
73
90
  });
74
91
  // Deliver callback: stream chunks back via MQTT
@@ -174,3 +191,52 @@ function formatCommandAsText(cmd) {
174
191
  }
175
192
  return parts.join(" ");
176
193
  }
194
+ const MIME_BY_EXT = {
195
+ ".jpg": "image/jpeg", ".jpeg": "image/jpeg", ".png": "image/png",
196
+ ".gif": "image/gif", ".webp": "image/webp", ".bmp": "image/bmp",
197
+ ".mp4": "video/mp4", ".mov": "video/quicktime", ".avi": "video/x-msvideo",
198
+ ".mp3": "audio/mpeg", ".ogg": "audio/ogg", ".wav": "audio/wav",
199
+ ".pdf": "application/pdf",
200
+ };
201
+ function guessMediaType(url) {
202
+ const pathname = new URL(url).pathname.toLowerCase();
203
+ for (const [ext, mime] of Object.entries(MIME_BY_EXT)) {
204
+ if (pathname.includes(ext))
205
+ return mime;
206
+ }
207
+ return "application/octet-stream";
208
+ }
209
+ function extFromMimeOrUrl(url, mime) {
210
+ for (const [ext, m] of Object.entries(MIME_BY_EXT)) {
211
+ if (m === mime)
212
+ return ext;
213
+ }
214
+ const pathname = new URL(url).pathname;
215
+ const dot = pathname.lastIndexOf(".");
216
+ if (dot >= 0) {
217
+ const ext = pathname.slice(dot).split(/[?#!/]/)[0];
218
+ if (ext.length <= 6)
219
+ return ext;
220
+ }
221
+ return ".bin";
222
+ }
223
+ let tempDir = null;
224
+ async function downloadToTemp(url, log) {
225
+ if (!tempDir) {
226
+ tempDir = await mkdtemp(join(tmpdir(), "elys-media-"));
227
+ }
228
+ const mime = guessMediaType(url);
229
+ const ext = extFromMimeOrUrl(url, mime);
230
+ const filename = `media_${Date.now()}${ext}`;
231
+ const filePath = join(tempDir, filename);
232
+ log(`[elys] downloading media: ${url} → ${filePath}`);
233
+ const resp = await fetch(url);
234
+ if (!resp.ok || !resp.body) {
235
+ throw new Error(`HTTP ${resp.status} downloading ${url}`);
236
+ }
237
+ const { Readable } = await import("node:stream");
238
+ const nodeStream = Readable.fromWeb(resp.body);
239
+ await pipeline(nodeStream, createWriteStream(filePath));
240
+ log(`[elys] downloaded media: ${filePath}`);
241
+ return filePath;
242
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "openclaw-elys",
3
- "version": "1.8.0",
3
+ "version": "1.8.1",
4
4
  "description": "OpenClaw Elys channel plugin — connects to Elys App",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",