@alfe.ai/openclaw-chat 0.8.0 → 0.8.2
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/dist/plugin2.cjs +44 -10
- package/dist/plugin2.js +44 -10
- package/package.json +4 -4
package/dist/plugin2.cjs
CHANGED
|
@@ -3,8 +3,8 @@ let node_fs_promises = require("node:fs/promises");
|
|
|
3
3
|
let node_path = require("node:path");
|
|
4
4
|
let node_os = require("node:os");
|
|
5
5
|
let _alfe_ai_chat = require("@alfe.ai/chat");
|
|
6
|
-
let node_crypto = require("node:crypto");
|
|
7
6
|
let _alfe_ai_agent_api_client = require("@alfe.ai/agent-api-client");
|
|
7
|
+
let node_crypto = require("node:crypto");
|
|
8
8
|
let _alfe_ai_config = require("@alfe.ai/config");
|
|
9
9
|
let node_fs = require("node:fs");
|
|
10
10
|
//#region src/outbound-media.ts
|
|
@@ -106,9 +106,9 @@ function extensionOf(p) {
|
|
|
106
106
|
function isRemoteUrl(ref) {
|
|
107
107
|
return /^https?:\/\//i.test(ref);
|
|
108
108
|
}
|
|
109
|
-
/** Non-http(s) URI schemes (data:, blob:,
|
|
110
|
-
*
|
|
111
|
-
*
|
|
109
|
+
/** Non-http(s) URI schemes (data:, blob:, …) — not local paths, nothing we
|
|
110
|
+
* can upload. Windows drive letters (C:\) are paths, not schemes.
|
|
111
|
+
* `media://inbound/<id>` is handled separately (resolveOpenClawMediaPath). */
|
|
112
112
|
function hasNonPathScheme(ref) {
|
|
113
113
|
if (/^file:\/\//i.test(ref)) return false;
|
|
114
114
|
if (/^[a-zA-Z]:[\\/]/.test(ref)) return false;
|
|
@@ -120,6 +120,34 @@ function resolveLocalMediaPath(ref) {
|
|
|
120
120
|
if (p === "~" || p.startsWith("~/")) p = (0, node_path.join)((0, node_os.homedir)(), p.slice(1));
|
|
121
121
|
return (0, node_path.isAbsolute)(p) ? p : (0, node_path.resolve)(process.cwd(), p);
|
|
122
122
|
}
|
|
123
|
+
const MEDIA_INBOUND_RE = /^media:\/\/inbound\/([^/\\]+)$/i;
|
|
124
|
+
/** Resolve a `media://inbound/<id>` URI to its media-store file, or null if
|
|
125
|
+
* the ref isn't one (or the id is unsafe). Traversal-hardened: the id must
|
|
126
|
+
* be a single path segment. */
|
|
127
|
+
function resolveOpenClawMediaPath(ref) {
|
|
128
|
+
const match = MEDIA_INBOUND_RE.exec(ref.trim());
|
|
129
|
+
if (!match) return null;
|
|
130
|
+
let id = match[1];
|
|
131
|
+
try {
|
|
132
|
+
id = decodeURIComponent(id);
|
|
133
|
+
} catch {}
|
|
134
|
+
if (!id || id === "." || id === ".." || /[/\\\0]/.test(id)) return null;
|
|
135
|
+
const stateDirOverride = process.env.OPENCLAW_STATE_DIR?.trim();
|
|
136
|
+
const home = process.env.OPENCLAW_HOME?.trim();
|
|
137
|
+
return (0, node_path.join)(stateDirOverride?.length ? stateDirOverride : (0, node_path.join)(home?.length ? home : (0, node_os.homedir)(), ".openclaw"), "media", "inbound", id);
|
|
138
|
+
}
|
|
139
|
+
/**
|
|
140
|
+
* Map an outbound ref to the local file we would upload: media-store path
|
|
141
|
+
* for `media://inbound/…`, filesystem path for path-shaped refs, null for
|
|
142
|
+
* remote URLs and unsupported schemes (data:, blob:, …).
|
|
143
|
+
*/
|
|
144
|
+
function toLocalUploadPath(ref) {
|
|
145
|
+
if (isRemoteUrl(ref)) return null;
|
|
146
|
+
const mediaStorePath = resolveOpenClawMediaPath(ref);
|
|
147
|
+
if (mediaStorePath) return mediaStorePath;
|
|
148
|
+
if (hasNonPathScheme(ref)) return null;
|
|
149
|
+
return resolveLocalMediaPath(ref);
|
|
150
|
+
}
|
|
123
151
|
async function uploadLocalFile(localPath, log, deps) {
|
|
124
152
|
const client = deps.getClient ? deps.getClient() : getUploadClient(log);
|
|
125
153
|
if (!client) return null;
|
|
@@ -197,11 +225,11 @@ function resolveOutboundMediaRef(ref, ctx, log, deps = {}) {
|
|
|
197
225
|
const trimmed = ref.trim();
|
|
198
226
|
if (!trimmed) return Promise.resolve(null);
|
|
199
227
|
if (isRemoteUrl(trimmed)) return Promise.resolve({ url: trimmed });
|
|
200
|
-
|
|
228
|
+
const localPath = toLocalUploadPath(trimmed);
|
|
229
|
+
if (!localPath) {
|
|
201
230
|
log.warn(`Outbound media ref has unsupported scheme — skipping: ${trimmed.slice(0, 120)}`);
|
|
202
231
|
return Promise.resolve(null);
|
|
203
232
|
}
|
|
204
|
-
const localPath = resolveLocalMediaPath(trimmed);
|
|
205
233
|
const cached = ctx.uploads.get(localPath);
|
|
206
234
|
if (cached) return cached;
|
|
207
235
|
const promise = ctx.uploads.size >= 10 ? (() => {
|
|
@@ -230,8 +258,8 @@ async function resolveOutboundMedia(refs, ctx, log, deps = {}) {
|
|
|
230
258
|
attachments.push(resolved);
|
|
231
259
|
continue;
|
|
232
260
|
}
|
|
233
|
-
|
|
234
|
-
|
|
261
|
+
const localPath = toLocalUploadPath(trimmed);
|
|
262
|
+
if (!localPath) continue;
|
|
235
263
|
if (!ctx.annotatedFailures.has(localPath)) {
|
|
236
264
|
ctx.annotatedFailures.add(localPath);
|
|
237
265
|
newFailures.push((0, node_path.basename)(localPath));
|
|
@@ -327,11 +355,16 @@ async function rewriteLocalMarkdownImages(text, ctx, log, deps = {}) {
|
|
|
327
355
|
const start = match.index;
|
|
328
356
|
out += seg.text.slice(cursor, start);
|
|
329
357
|
cursor = start + full.length;
|
|
330
|
-
if (isRemoteUrl(src)
|
|
358
|
+
if (isRemoteUrl(src)) {
|
|
359
|
+
out += full;
|
|
360
|
+
continue;
|
|
361
|
+
}
|
|
362
|
+
const localPath = toLocalUploadPath(src);
|
|
363
|
+
if (!localPath) {
|
|
331
364
|
out += full;
|
|
332
365
|
continue;
|
|
333
366
|
}
|
|
334
|
-
const ext = extensionOf(
|
|
367
|
+
const ext = extensionOf(localPath.split("?")[0]);
|
|
335
368
|
if (!ext || !IMAGE_EXTENSIONS.has(ext)) {
|
|
336
369
|
out += full;
|
|
337
370
|
continue;
|
|
@@ -2652,6 +2685,7 @@ const plugin = {
|
|
|
2652
2685
|
api.registerChannel(alfeChannel);
|
|
2653
2686
|
log.info(`Registered channel: ${alfeChannel.id}`);
|
|
2654
2687
|
if (typeof api.registerTool === "function") {
|
|
2688
|
+
(0, _alfe_ai_agent_api_client.installToolErrorCapture)(api, { plugin: "openclaw-chat" });
|
|
2655
2689
|
const present = async (args) => {
|
|
2656
2690
|
const clean = sanitizeComponents(args.components);
|
|
2657
2691
|
if (clean.length === 0) throw new Error("No valid components: link_button requires a `label` and an https `url` on an Alfe-owned host");
|
package/dist/plugin2.js
CHANGED
|
@@ -3,8 +3,8 @@ import { mkdir, open, readFile, readdir, stat, unlink, writeFile } from "node:fs
|
|
|
3
3
|
import { basename, dirname, isAbsolute, join, resolve } from "node:path";
|
|
4
4
|
import { homedir } from "node:os";
|
|
5
5
|
import { ChatServiceClient, resolveAlfeChat } from "@alfe.ai/chat";
|
|
6
|
+
import { AgentApiClient, installToolErrorCapture } from "@alfe.ai/agent-api-client";
|
|
6
7
|
import { randomUUID } from "node:crypto";
|
|
7
|
-
import { AgentApiClient } from "@alfe.ai/agent-api-client";
|
|
8
8
|
import { resolveConfig } from "@alfe.ai/config";
|
|
9
9
|
import { existsSync } from "node:fs";
|
|
10
10
|
//#region src/outbound-media.ts
|
|
@@ -106,9 +106,9 @@ function extensionOf(p) {
|
|
|
106
106
|
function isRemoteUrl(ref) {
|
|
107
107
|
return /^https?:\/\//i.test(ref);
|
|
108
108
|
}
|
|
109
|
-
/** Non-http(s) URI schemes (data:, blob:,
|
|
110
|
-
*
|
|
111
|
-
*
|
|
109
|
+
/** Non-http(s) URI schemes (data:, blob:, …) — not local paths, nothing we
|
|
110
|
+
* can upload. Windows drive letters (C:\) are paths, not schemes.
|
|
111
|
+
* `media://inbound/<id>` is handled separately (resolveOpenClawMediaPath). */
|
|
112
112
|
function hasNonPathScheme(ref) {
|
|
113
113
|
if (/^file:\/\//i.test(ref)) return false;
|
|
114
114
|
if (/^[a-zA-Z]:[\\/]/.test(ref)) return false;
|
|
@@ -120,6 +120,34 @@ function resolveLocalMediaPath(ref) {
|
|
|
120
120
|
if (p === "~" || p.startsWith("~/")) p = join(homedir(), p.slice(1));
|
|
121
121
|
return isAbsolute(p) ? p : resolve(process.cwd(), p);
|
|
122
122
|
}
|
|
123
|
+
const MEDIA_INBOUND_RE = /^media:\/\/inbound\/([^/\\]+)$/i;
|
|
124
|
+
/** Resolve a `media://inbound/<id>` URI to its media-store file, or null if
|
|
125
|
+
* the ref isn't one (or the id is unsafe). Traversal-hardened: the id must
|
|
126
|
+
* be a single path segment. */
|
|
127
|
+
function resolveOpenClawMediaPath(ref) {
|
|
128
|
+
const match = MEDIA_INBOUND_RE.exec(ref.trim());
|
|
129
|
+
if (!match) return null;
|
|
130
|
+
let id = match[1];
|
|
131
|
+
try {
|
|
132
|
+
id = decodeURIComponent(id);
|
|
133
|
+
} catch {}
|
|
134
|
+
if (!id || id === "." || id === ".." || /[/\\\0]/.test(id)) return null;
|
|
135
|
+
const stateDirOverride = process.env.OPENCLAW_STATE_DIR?.trim();
|
|
136
|
+
const home = process.env.OPENCLAW_HOME?.trim();
|
|
137
|
+
return join(stateDirOverride?.length ? stateDirOverride : join(home?.length ? home : homedir(), ".openclaw"), "media", "inbound", id);
|
|
138
|
+
}
|
|
139
|
+
/**
|
|
140
|
+
* Map an outbound ref to the local file we would upload: media-store path
|
|
141
|
+
* for `media://inbound/…`, filesystem path for path-shaped refs, null for
|
|
142
|
+
* remote URLs and unsupported schemes (data:, blob:, …).
|
|
143
|
+
*/
|
|
144
|
+
function toLocalUploadPath(ref) {
|
|
145
|
+
if (isRemoteUrl(ref)) return null;
|
|
146
|
+
const mediaStorePath = resolveOpenClawMediaPath(ref);
|
|
147
|
+
if (mediaStorePath) return mediaStorePath;
|
|
148
|
+
if (hasNonPathScheme(ref)) return null;
|
|
149
|
+
return resolveLocalMediaPath(ref);
|
|
150
|
+
}
|
|
123
151
|
async function uploadLocalFile(localPath, log, deps) {
|
|
124
152
|
const client = deps.getClient ? deps.getClient() : getUploadClient(log);
|
|
125
153
|
if (!client) return null;
|
|
@@ -197,11 +225,11 @@ function resolveOutboundMediaRef(ref, ctx, log, deps = {}) {
|
|
|
197
225
|
const trimmed = ref.trim();
|
|
198
226
|
if (!trimmed) return Promise.resolve(null);
|
|
199
227
|
if (isRemoteUrl(trimmed)) return Promise.resolve({ url: trimmed });
|
|
200
|
-
|
|
228
|
+
const localPath = toLocalUploadPath(trimmed);
|
|
229
|
+
if (!localPath) {
|
|
201
230
|
log.warn(`Outbound media ref has unsupported scheme — skipping: ${trimmed.slice(0, 120)}`);
|
|
202
231
|
return Promise.resolve(null);
|
|
203
232
|
}
|
|
204
|
-
const localPath = resolveLocalMediaPath(trimmed);
|
|
205
233
|
const cached = ctx.uploads.get(localPath);
|
|
206
234
|
if (cached) return cached;
|
|
207
235
|
const promise = ctx.uploads.size >= 10 ? (() => {
|
|
@@ -230,8 +258,8 @@ async function resolveOutboundMedia(refs, ctx, log, deps = {}) {
|
|
|
230
258
|
attachments.push(resolved);
|
|
231
259
|
continue;
|
|
232
260
|
}
|
|
233
|
-
|
|
234
|
-
|
|
261
|
+
const localPath = toLocalUploadPath(trimmed);
|
|
262
|
+
if (!localPath) continue;
|
|
235
263
|
if (!ctx.annotatedFailures.has(localPath)) {
|
|
236
264
|
ctx.annotatedFailures.add(localPath);
|
|
237
265
|
newFailures.push(basename(localPath));
|
|
@@ -327,11 +355,16 @@ async function rewriteLocalMarkdownImages(text, ctx, log, deps = {}) {
|
|
|
327
355
|
const start = match.index;
|
|
328
356
|
out += seg.text.slice(cursor, start);
|
|
329
357
|
cursor = start + full.length;
|
|
330
|
-
if (isRemoteUrl(src)
|
|
358
|
+
if (isRemoteUrl(src)) {
|
|
359
|
+
out += full;
|
|
360
|
+
continue;
|
|
361
|
+
}
|
|
362
|
+
const localPath = toLocalUploadPath(src);
|
|
363
|
+
if (!localPath) {
|
|
331
364
|
out += full;
|
|
332
365
|
continue;
|
|
333
366
|
}
|
|
334
|
-
const ext = extensionOf(
|
|
367
|
+
const ext = extensionOf(localPath.split("?")[0]);
|
|
335
368
|
if (!ext || !IMAGE_EXTENSIONS.has(ext)) {
|
|
336
369
|
out += full;
|
|
337
370
|
continue;
|
|
@@ -2652,6 +2685,7 @@ const plugin = {
|
|
|
2652
2685
|
api.registerChannel(alfeChannel);
|
|
2653
2686
|
log.info(`Registered channel: ${alfeChannel.id}`);
|
|
2654
2687
|
if (typeof api.registerTool === "function") {
|
|
2688
|
+
installToolErrorCapture(api, { plugin: "openclaw-chat" });
|
|
2655
2689
|
const present = async (args) => {
|
|
2656
2690
|
const clean = sanitizeComponents(args.components);
|
|
2657
2691
|
if (clean.length === 0) throw new Error("No valid components: link_button requires a `label` and an https `url` on an Alfe-owned host");
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@alfe.ai/openclaw-chat",
|
|
3
|
-
"version": "0.8.
|
|
3
|
+
"version": "0.8.2",
|
|
4
4
|
"description": "OpenClaw chat plugin for Alfe — web widget and mobile app channels",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/plugin.js",
|
|
@@ -27,9 +27,9 @@
|
|
|
27
27
|
"openclaw.plugin.json"
|
|
28
28
|
],
|
|
29
29
|
"dependencies": {
|
|
30
|
-
"@alfe.ai/agent-api-client": "^0.
|
|
31
|
-
"@alfe.ai/
|
|
32
|
-
"@alfe.ai/
|
|
30
|
+
"@alfe.ai/agent-api-client": "^0.8.0",
|
|
31
|
+
"@alfe.ai/config": "^0.3.0",
|
|
32
|
+
"@alfe.ai/chat": "^0.0.14"
|
|
33
33
|
},
|
|
34
34
|
"peerDependencies": {
|
|
35
35
|
"openclaw": ">=2026.3.0"
|