@workclaw/openclaw-workclaw 1.0.19 → 1.0.201
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/README.md +364 -364
- package/dist/index.js +2 -1
- package/dist/src/api/workspace.js +2 -0
- package/dist/src/channel.js +1 -0
- package/dist/src/gateway/agent-handlers.js +87 -87
- package/dist/src/gateway/config-writer.js +4 -14
- package/dist/src/gateway/cron-tasks-handler.d.ts +19 -0
- package/dist/src/gateway/cron-tasks-handler.js +188 -0
- package/dist/src/gateway/message-context.d.ts +8 -4
- package/dist/src/gateway/message-context.js +148 -138
- package/dist/src/gateway/message-dispatcher.d.ts +0 -1
- package/dist/src/gateway/message-dispatcher.js +383 -323
- package/dist/src/gateway/reconnect.js +4 -0
- package/dist/src/gateway/skills-handler.js +187 -148
- package/dist/src/gateway/workclaw-gateway.d.ts +1 -1
- package/dist/src/gateway/workclaw-gateway.js +47 -33
- package/dist/src/tools/openclaw-workclaw-cron/src/update/params.js +17 -17
- package/package.json +53 -62
|
@@ -1,10 +1,10 @@
|
|
|
1
|
-
import fs from
|
|
2
|
-
import
|
|
3
|
-
import
|
|
4
|
-
import
|
|
5
|
-
import
|
|
1
|
+
import fs from 'node:fs/promises';
|
|
2
|
+
import http from 'node:http';
|
|
3
|
+
import https from 'node:https';
|
|
4
|
+
import os from 'node:os';
|
|
5
|
+
import path from 'node:path';
|
|
6
6
|
/** Channel/plugin name constant */
|
|
7
|
-
export const OPENCLAW_WORKCLAW_CHANNEL =
|
|
7
|
+
export const OPENCLAW_WORKCLAW_CHANNEL = 'openclaw-workclaw';
|
|
8
8
|
export function parseInboundMessage(data, allowRawJsonPayload) {
|
|
9
9
|
let input = {};
|
|
10
10
|
try {
|
|
@@ -13,17 +13,17 @@ export function parseInboundMessage(data, allowRawJsonPayload) {
|
|
|
13
13
|
catch {
|
|
14
14
|
// Not JSON, treat as plain text
|
|
15
15
|
}
|
|
16
|
-
const text = typeof input.text ===
|
|
16
|
+
const text = typeof input.text === 'string'
|
|
17
17
|
? input.text
|
|
18
|
-
: typeof input.message ===
|
|
18
|
+
: typeof input.message === 'string'
|
|
19
19
|
? input.message
|
|
20
|
-
: typeof input.body ===
|
|
20
|
+
: typeof input.body === 'string'
|
|
21
21
|
? input.body
|
|
22
22
|
: data;
|
|
23
|
-
const to = String(input.to ?? input.chatId ?? input.target ??
|
|
24
|
-
const from = String(input.from ?? input.senderId ?? input.userId ??
|
|
25
|
-
const chatType = String(input.chatType ||
|
|
26
|
-
const messageId = String(input.messageId ?? input.id ??
|
|
23
|
+
const to = String(input.to ?? input.chatId ?? input.target ?? '');
|
|
24
|
+
const from = String(input.from ?? input.senderId ?? input.userId ?? '');
|
|
25
|
+
const chatType = String(input.chatType || '').toLowerCase() === 'group' ? 'group' : 'dm';
|
|
26
|
+
const messageId = String(input.messageId ?? input.id ?? '');
|
|
27
27
|
const rawPayload = allowRawJsonPayload ? JSON.stringify(input ?? {}) : data;
|
|
28
28
|
return {
|
|
29
29
|
text,
|
|
@@ -35,7 +35,7 @@ export function parseInboundMessage(data, allowRawJsonPayload) {
|
|
|
35
35
|
};
|
|
36
36
|
}
|
|
37
37
|
export function parseWorkClawMessage(data, log) {
|
|
38
|
-
//{"specVersion":"1.0","type":"EVENT","metadata":{"time":"1773749163831","event":"INIT_AGENT","contentType":"application/json"},"data":"\"12557533957824965\""}
|
|
38
|
+
// {"specVersion":"1.0","type":"EVENT","metadata":{"time":"1773749163831","event":"INIT_AGENT","contentType":"application/json"},"data":"\"12557533957824965\""}
|
|
39
39
|
let input = {};
|
|
40
40
|
try {
|
|
41
41
|
input = data ? JSON.parse(data) : {};
|
|
@@ -45,19 +45,19 @@ export function parseWorkClawMessage(data, log) {
|
|
|
45
45
|
return null;
|
|
46
46
|
}
|
|
47
47
|
// Handle plain text ping
|
|
48
|
-
if (typeof input ===
|
|
48
|
+
if (typeof input === 'string' && input.toLowerCase() === 'ping') {
|
|
49
49
|
return {
|
|
50
|
-
type:
|
|
50
|
+
type: 'ping',
|
|
51
51
|
pongData: input,
|
|
52
52
|
};
|
|
53
53
|
}
|
|
54
|
-
const envelope = input && typeof input ===
|
|
54
|
+
const envelope = input && typeof input === 'object' ? input : { data: input };
|
|
55
55
|
const topic = envelope?.metadata?.topic;
|
|
56
|
-
const type = String(envelope?.type ||
|
|
57
|
-
const topicLower = String(topic ||
|
|
58
|
-
if (type ===
|
|
56
|
+
const type = String(envelope?.type || '').trim().toUpperCase();
|
|
57
|
+
const topicLower = String(topic || '').trim().toLowerCase();
|
|
58
|
+
if (type === 'INIT_AGENT') {
|
|
59
59
|
let parsedData = envelope?.data;
|
|
60
|
-
if (typeof parsedData ===
|
|
60
|
+
if (typeof parsedData === 'string') {
|
|
61
61
|
try {
|
|
62
62
|
parsedData = JSON.parse(parsedData);
|
|
63
63
|
}
|
|
@@ -66,34 +66,34 @@ export function parseWorkClawMessage(data, log) {
|
|
|
66
66
|
}
|
|
67
67
|
}
|
|
68
68
|
return {
|
|
69
|
-
type:
|
|
69
|
+
type: 'init_agent',
|
|
70
70
|
eventData: parsedData,
|
|
71
71
|
};
|
|
72
72
|
}
|
|
73
73
|
// Handle SYSTEM messages
|
|
74
|
-
if (type ===
|
|
75
|
-
if (topicLower ===
|
|
74
|
+
if (type === 'SYSTEM') {
|
|
75
|
+
if (topicLower === 'ping') {
|
|
76
76
|
return {
|
|
77
|
-
type:
|
|
77
|
+
type: 'ping',
|
|
78
78
|
pongData: envelope?.data,
|
|
79
79
|
};
|
|
80
80
|
}
|
|
81
|
-
if (topicLower ===
|
|
81
|
+
if (topicLower === 'disconnect') {
|
|
82
82
|
return {
|
|
83
|
-
type:
|
|
83
|
+
type: 'disconnect',
|
|
84
84
|
};
|
|
85
85
|
}
|
|
86
86
|
// Unknown system message type, ignore
|
|
87
87
|
return {
|
|
88
|
-
type:
|
|
88
|
+
type: 'ignored',
|
|
89
89
|
};
|
|
90
90
|
}
|
|
91
91
|
// Handle EVENT messages (AGENT_CREATED, AGENT_UPDATED, AGENT_DELETED, TOOLS_LIST, SKILLS_LIST, INIT_AGENT)
|
|
92
|
-
if (type ===
|
|
93
|
-
const eventType = String(envelope?.metadata?.event ||
|
|
92
|
+
if (type === 'EVENT') {
|
|
93
|
+
const eventType = String(envelope?.metadata?.event || '').trim().toUpperCase();
|
|
94
94
|
// 解析 data 字段(可能是 JSON 字符串)
|
|
95
95
|
let parsedData = envelope?.data;
|
|
96
|
-
if (typeof parsedData ===
|
|
96
|
+
if (typeof parsedData === 'string') {
|
|
97
97
|
try {
|
|
98
98
|
parsedData = JSON.parse(parsedData);
|
|
99
99
|
}
|
|
@@ -101,69 +101,79 @@ export function parseWorkClawMessage(data, log) {
|
|
|
101
101
|
// 解析失败,保持原样
|
|
102
102
|
}
|
|
103
103
|
}
|
|
104
|
-
if (eventType ===
|
|
104
|
+
if (eventType === 'AGENT_CREATED') {
|
|
105
105
|
return {
|
|
106
|
-
type:
|
|
106
|
+
type: 'agent_created',
|
|
107
107
|
eventData: parsedData,
|
|
108
108
|
};
|
|
109
109
|
}
|
|
110
|
-
if (eventType ===
|
|
110
|
+
if (eventType === 'AGENT_UPDATED') {
|
|
111
111
|
return {
|
|
112
|
-
type:
|
|
112
|
+
type: 'agent_updated',
|
|
113
113
|
eventData: parsedData,
|
|
114
114
|
};
|
|
115
115
|
}
|
|
116
|
-
if (eventType ===
|
|
116
|
+
if (eventType === 'AGENT_DELETED') {
|
|
117
117
|
return {
|
|
118
|
-
type:
|
|
118
|
+
type: 'agent_deleted',
|
|
119
119
|
eventData: parsedData,
|
|
120
120
|
};
|
|
121
121
|
}
|
|
122
|
-
if (eventType ===
|
|
122
|
+
if (eventType === 'TOOLS_LIST') {
|
|
123
123
|
return {
|
|
124
|
-
type:
|
|
124
|
+
type: 'tools_list',
|
|
125
125
|
eventData: parsedData,
|
|
126
126
|
};
|
|
127
127
|
}
|
|
128
|
-
if (eventType ===
|
|
128
|
+
if (eventType === 'SKILLS_LIST') {
|
|
129
129
|
return {
|
|
130
|
-
type:
|
|
130
|
+
type: 'skills_list',
|
|
131
131
|
eventData: parsedData,
|
|
132
132
|
};
|
|
133
133
|
}
|
|
134
|
-
if (eventType ===
|
|
134
|
+
if (eventType === 'INIT_AGENT') {
|
|
135
135
|
return {
|
|
136
|
-
type:
|
|
136
|
+
type: 'init_agent',
|
|
137
137
|
eventData: parsedData,
|
|
138
138
|
};
|
|
139
139
|
}
|
|
140
|
-
if (eventType ===
|
|
141
|
-
const eventTopic = String(parsedData?.dotype ||
|
|
140
|
+
if (eventType === 'SKILL_DO_REMIND') {
|
|
141
|
+
const eventTopic = String(parsedData?.dotype || '').toLowerCase();
|
|
142
142
|
return {
|
|
143
|
-
type:
|
|
143
|
+
type: 'skills_event',
|
|
144
144
|
eventData: {
|
|
145
|
-
topic:
|
|
146
|
-
requestId:
|
|
147
|
-
callbackUrl:
|
|
148
|
-
authToken:
|
|
145
|
+
topic: `skills/${eventTopic}`,
|
|
146
|
+
requestId: '',
|
|
147
|
+
callbackUrl: '',
|
|
148
|
+
authToken: '',
|
|
149
|
+
data: parsedData,
|
|
150
|
+
},
|
|
151
|
+
};
|
|
152
|
+
}
|
|
153
|
+
if (eventType === 'CRON_REMIND') {
|
|
154
|
+
const eventTopic = String(parsedData?.dotype || '').toLowerCase();
|
|
155
|
+
return {
|
|
156
|
+
type: 'cron_task_event',
|
|
157
|
+
cronTaskEvent: {
|
|
158
|
+
dotype: `${eventTopic}`,
|
|
149
159
|
data: parsedData,
|
|
150
160
|
},
|
|
151
161
|
};
|
|
152
162
|
}
|
|
153
163
|
// Unknown event type, ignore
|
|
154
164
|
return {
|
|
155
|
-
type:
|
|
165
|
+
type: 'ignored',
|
|
156
166
|
};
|
|
157
167
|
}
|
|
158
168
|
// Only process CALLBACK type with AGENT_MESSAGE topic
|
|
159
|
-
if (type !==
|
|
169
|
+
if (type !== 'CALLBACK' || String(topic || '').trim().toUpperCase() !== 'AGENT_MESSAGE') {
|
|
160
170
|
return {
|
|
161
|
-
type:
|
|
171
|
+
type: 'ignored',
|
|
162
172
|
};
|
|
163
173
|
}
|
|
164
174
|
// 解析 data 字段(可能是 JSON 字符串)
|
|
165
175
|
let payload = envelope?.data || {};
|
|
166
|
-
if (typeof payload ===
|
|
176
|
+
if (typeof payload === 'string') {
|
|
167
177
|
try {
|
|
168
178
|
payload = JSON.parse(payload);
|
|
169
179
|
}
|
|
@@ -171,11 +181,11 @@ export function parseWorkClawMessage(data, log) {
|
|
|
171
181
|
// 解析失败,保持原样
|
|
172
182
|
}
|
|
173
183
|
}
|
|
174
|
-
const text = typeof payload?.message?.content ===
|
|
184
|
+
const text = typeof payload?.message?.content === 'string'
|
|
175
185
|
? payload.message.content
|
|
176
|
-
: typeof payload?.message ===
|
|
186
|
+
: typeof payload?.message === 'string'
|
|
177
187
|
? payload.message
|
|
178
|
-
:
|
|
188
|
+
: '';
|
|
179
189
|
// 判断文件是否为图片
|
|
180
190
|
function isImageFile(url) {
|
|
181
191
|
const imageExtensions = ['.jpg', '.jpeg', '.png', '.gif', '.webp', '.bmp', '.svg', '.ico'];
|
|
@@ -184,18 +194,18 @@ export function parseWorkClawMessage(data, log) {
|
|
|
184
194
|
}
|
|
185
195
|
// 解析 fileUrl 列表到 attachments(供 SDK 和 AI 使用)
|
|
186
196
|
const fileUrlList = Array.isArray(payload?.fileUrl) ? payload.fileUrl : [];
|
|
187
|
-
const attachments = fileUrlList.map(
|
|
197
|
+
const attachments = fileUrlList.map(url => ({
|
|
188
198
|
url,
|
|
189
199
|
fileId: payload?.fileId ? String(payload.fileId) : '',
|
|
190
200
|
type: isImageFile(url) ? 'image' : 'file',
|
|
191
201
|
}));
|
|
192
202
|
return {
|
|
193
|
-
type:
|
|
203
|
+
type: 'agent_message',
|
|
194
204
|
message: {
|
|
195
205
|
text,
|
|
196
|
-
userId: String(payload?.userId ??
|
|
197
|
-
openConversationId: String(payload?.conversationId ??
|
|
198
|
-
messageId: String(payload?.msgId ??
|
|
206
|
+
userId: String(payload?.userId ?? ''),
|
|
207
|
+
openConversationId: String(payload?.conversationId ?? ''),
|
|
208
|
+
messageId: String(payload?.msgId ?? ''),
|
|
199
209
|
agentId: payload?.agentId,
|
|
200
210
|
attachments: attachments.length > 0 ? attachments : undefined,
|
|
201
211
|
},
|
|
@@ -206,97 +216,97 @@ export function parseWorkClawMessage(data, log) {
|
|
|
206
216
|
*/
|
|
207
217
|
function detectMediaType(url) {
|
|
208
218
|
const lower = url.toLowerCase();
|
|
209
|
-
if (lower.endsWith(
|
|
210
|
-
|| lower.endsWith(
|
|
211
|
-
|| lower.endsWith(
|
|
212
|
-
return
|
|
219
|
+
if (lower.endsWith('.jpg') || lower.endsWith('.jpeg') || lower.endsWith('.png')
|
|
220
|
+
|| lower.endsWith('.gif') || lower.endsWith('.webp') || lower.endsWith('.bmp')
|
|
221
|
+
|| lower.endsWith('.svg') || lower.endsWith('.ico')) {
|
|
222
|
+
return 'image';
|
|
213
223
|
}
|
|
214
|
-
if (lower.endsWith(
|
|
215
|
-
|| lower.endsWith(
|
|
216
|
-
return
|
|
224
|
+
if (lower.endsWith('.mp3') || lower.endsWith('.wav') || lower.endsWith('.amr')
|
|
225
|
+
|| lower.endsWith('.m4a') || lower.endsWith('.ogg')) {
|
|
226
|
+
return 'audio';
|
|
217
227
|
}
|
|
218
|
-
if (lower.endsWith(
|
|
219
|
-
|| lower.endsWith(
|
|
220
|
-
return
|
|
228
|
+
if (lower.endsWith('.mp4') || lower.endsWith('.avi') || lower.endsWith('.mov')
|
|
229
|
+
|| lower.endsWith('.mkv') || lower.endsWith('.webm')) {
|
|
230
|
+
return 'video';
|
|
221
231
|
}
|
|
222
|
-
if (lower.endsWith(
|
|
223
|
-
return
|
|
224
|
-
if (lower.endsWith(
|
|
225
|
-
return
|
|
226
|
-
if (lower.endsWith(
|
|
227
|
-
return
|
|
228
|
-
if (lower.endsWith(
|
|
229
|
-
return
|
|
230
|
-
if (lower.endsWith(
|
|
231
|
-
return
|
|
232
|
-
if (lower.endsWith(
|
|
233
|
-
return
|
|
234
|
-
if (lower.endsWith(
|
|
235
|
-
return
|
|
236
|
-
if (lower.endsWith(
|
|
237
|
-
return
|
|
238
|
-
if (lower.endsWith(
|
|
239
|
-
return
|
|
240
|
-
if (lower.endsWith(
|
|
241
|
-
return
|
|
242
|
-
return
|
|
232
|
+
if (lower.endsWith('.pdf'))
|
|
233
|
+
return 'application/pdf';
|
|
234
|
+
if (lower.endsWith('.docx'))
|
|
235
|
+
return 'application/vnd.openxmlformats-officedocument.wordprocessingml.document';
|
|
236
|
+
if (lower.endsWith('.xlsx'))
|
|
237
|
+
return 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet';
|
|
238
|
+
if (lower.endsWith('.pptx'))
|
|
239
|
+
return 'application/vnd.openxmlformats-officedocument.presentationml.presentation';
|
|
240
|
+
if (lower.endsWith('.doc'))
|
|
241
|
+
return 'application/msword';
|
|
242
|
+
if (lower.endsWith('.xls'))
|
|
243
|
+
return 'application/vnd.ms-excel';
|
|
244
|
+
if (lower.endsWith('.ppt'))
|
|
245
|
+
return 'application/vnd.ms-powerpoint';
|
|
246
|
+
if (lower.endsWith('.txt'))
|
|
247
|
+
return 'text/plain';
|
|
248
|
+
if (lower.endsWith('.zip'))
|
|
249
|
+
return 'application/zip';
|
|
250
|
+
if (lower.endsWith('.rar'))
|
|
251
|
+
return 'application/x-rar-compressed';
|
|
252
|
+
return 'application/octet-stream';
|
|
243
253
|
}
|
|
244
254
|
/**
|
|
245
255
|
* 从 URL 扩展名获取人类可读的类型标签
|
|
246
256
|
*/
|
|
247
257
|
function getMediaTypeLabel(url) {
|
|
248
258
|
const lower = url.toLowerCase();
|
|
249
|
-
if (lower.endsWith(
|
|
250
|
-
return
|
|
251
|
-
if (lower.endsWith(
|
|
252
|
-
return
|
|
253
|
-
if (lower.endsWith(
|
|
254
|
-
return
|
|
255
|
-
if (lower.endsWith(
|
|
256
|
-
return
|
|
257
|
-
if (lower.endsWith(
|
|
258
|
-
return
|
|
259
|
-
if (lower.endsWith(
|
|
260
|
-
return
|
|
261
|
-
if (lower.endsWith(
|
|
262
|
-
return
|
|
263
|
-
if (lower.endsWith(
|
|
264
|
-
return
|
|
265
|
-
if (lower.endsWith(
|
|
266
|
-
return
|
|
267
|
-
if (lower.endsWith(
|
|
268
|
-
return
|
|
269
|
-
if (lower.endsWith(
|
|
270
|
-
return
|
|
271
|
-
if (lower.endsWith(
|
|
272
|
-
return
|
|
273
|
-
if (lower.endsWith(
|
|
274
|
-
return
|
|
275
|
-
if (lower.endsWith(
|
|
276
|
-
return
|
|
277
|
-
if (lower.endsWith(
|
|
278
|
-
return
|
|
279
|
-
if (lower.endsWith(
|
|
280
|
-
return
|
|
281
|
-
return
|
|
259
|
+
if (lower.endsWith('.pdf'))
|
|
260
|
+
return 'PDF';
|
|
261
|
+
if (lower.endsWith('.docx'))
|
|
262
|
+
return 'Word';
|
|
263
|
+
if (lower.endsWith('.doc'))
|
|
264
|
+
return 'Word';
|
|
265
|
+
if (lower.endsWith('.xlsx'))
|
|
266
|
+
return 'Excel';
|
|
267
|
+
if (lower.endsWith('.xls'))
|
|
268
|
+
return 'Excel';
|
|
269
|
+
if (lower.endsWith('.pptx'))
|
|
270
|
+
return 'PowerPoint';
|
|
271
|
+
if (lower.endsWith('.ppt'))
|
|
272
|
+
return 'PowerPoint';
|
|
273
|
+
if (lower.endsWith('.txt'))
|
|
274
|
+
return '文本';
|
|
275
|
+
if (lower.endsWith('.jpg') || lower.endsWith('.jpeg'))
|
|
276
|
+
return 'JPG图片';
|
|
277
|
+
if (lower.endsWith('.png'))
|
|
278
|
+
return 'PNG图片';
|
|
279
|
+
if (lower.endsWith('.gif'))
|
|
280
|
+
return 'GIF图片';
|
|
281
|
+
if (lower.endsWith('.webp'))
|
|
282
|
+
return 'WebP图片';
|
|
283
|
+
if (lower.endsWith('.mp3'))
|
|
284
|
+
return '音频';
|
|
285
|
+
if (lower.endsWith('.wav'))
|
|
286
|
+
return '音频';
|
|
287
|
+
if (lower.endsWith('.mp4'))
|
|
288
|
+
return '视频';
|
|
289
|
+
if (lower.endsWith('.zip') || lower.endsWith('.rar'))
|
|
290
|
+
return '压缩包';
|
|
291
|
+
return '文件';
|
|
282
292
|
}
|
|
283
293
|
/**
|
|
284
294
|
* 下载文件到本地下载目录(按 accountId/peerId 隔离),返回本地路径
|
|
285
295
|
*/
|
|
286
296
|
async function downloadFileToLocal(url, accountId, peerId, log) {
|
|
287
|
-
const downloadsDir = path.join(os.homedir(),
|
|
297
|
+
const downloadsDir = path.join(os.homedir(), '.openclaw', 'media', 'workclaw', 'downloads', accountId, peerId);
|
|
288
298
|
try {
|
|
289
299
|
await fs.mkdir(downloadsDir, { recursive: true });
|
|
290
300
|
}
|
|
291
301
|
catch { }
|
|
292
302
|
const urlObj = new URL(url);
|
|
293
|
-
const fileName = path.basename(urlObj.pathname) ||
|
|
303
|
+
const fileName = path.basename(urlObj.pathname) || 'file';
|
|
294
304
|
const ext = path.extname(fileName);
|
|
295
305
|
const stem = path.basename(fileName, ext);
|
|
296
306
|
const localFileName = `${stem}_${Date.now()}${ext}`;
|
|
297
307
|
const localFilePath = path.join(downloadsDir, localFileName);
|
|
298
308
|
return new Promise((resolve) => {
|
|
299
|
-
const client = url.startsWith(
|
|
309
|
+
const client = url.startsWith('https:') ? https : http;
|
|
300
310
|
const req = client.get(url, (res) => {
|
|
301
311
|
if (res.statusCode !== 200) {
|
|
302
312
|
log?.warn?.(`downloadFile: HTTP ${res.statusCode} for ${url}`);
|
|
@@ -304,8 +314,8 @@ async function downloadFileToLocal(url, accountId, peerId, log) {
|
|
|
304
314
|
return;
|
|
305
315
|
}
|
|
306
316
|
const chunks = [];
|
|
307
|
-
res.on(
|
|
308
|
-
res.on(
|
|
317
|
+
res.on('data', (chunk) => chunks.push(chunk));
|
|
318
|
+
res.on('end', async () => {
|
|
309
319
|
try {
|
|
310
320
|
const buffer = Buffer.concat(chunks);
|
|
311
321
|
await fs.writeFile(localFilePath, buffer);
|
|
@@ -317,12 +327,12 @@ async function downloadFileToLocal(url, accountId, peerId, log) {
|
|
|
317
327
|
resolve(null);
|
|
318
328
|
}
|
|
319
329
|
});
|
|
320
|
-
res.on(
|
|
330
|
+
res.on('error', (err) => {
|
|
321
331
|
log?.error?.(`downloadFile: read error: ${String(err)}`);
|
|
322
332
|
resolve(null);
|
|
323
333
|
});
|
|
324
334
|
});
|
|
325
|
-
req.on(
|
|
335
|
+
req.on('error', (err) => {
|
|
326
336
|
log?.error?.(`downloadFile: request error: ${String(err)}`);
|
|
327
337
|
resolve(null);
|
|
328
338
|
});
|
|
@@ -360,10 +370,10 @@ export async function buildInboundContext(message, accountId, config, agentId, p
|
|
|
360
370
|
}
|
|
361
371
|
const peerId = message.from;
|
|
362
372
|
const computedSessionKey = existingSessionKey
|
|
363
|
-
|| (agentId && agentId !==
|
|
373
|
+
|| (agentId && agentId !== 'main'
|
|
364
374
|
? `agent:${agentId}:openclaw-workclaw:${accountId}:direct:${peerId}`
|
|
365
375
|
: undefined);
|
|
366
|
-
const sessionKey = computedSessionKey || (agentId && agentId !==
|
|
376
|
+
const sessionKey = computedSessionKey || (agentId && agentId !== 'main'
|
|
367
377
|
? `agent:${agentId}:openclaw-workclaw:${accountId}:direct:${peerId}`
|
|
368
378
|
: undefined);
|
|
369
379
|
// Basic context (used as base for both paths)
|
|
@@ -423,8 +433,8 @@ export async function buildInboundContext(message, accountId, config, agentId, p
|
|
|
423
433
|
// 兼容 Windows 路径格式(C:\Users\用户名\.openclaw\... 或 C:\Users\用户名.openclaw\...)
|
|
424
434
|
const homeDir = os.homedir().replace(/\\/g, '/').trim(); // 统一用正斜杠,去除尾随空格
|
|
425
435
|
const normalizedLocalPath = localPath.replace(/\\/g, '/');
|
|
426
|
-
const relativePath = normalizedLocalPath.startsWith(homeDir
|
|
427
|
-
?
|
|
436
|
+
const relativePath = normalizedLocalPath.startsWith(`${homeDir}/.openclaw/`)
|
|
437
|
+
? `/${normalizedLocalPath.slice(homeDir.length + 1)}` // 用 / 开头表示根相对路径
|
|
428
438
|
: normalizedLocalPath;
|
|
429
439
|
mediaUrls.push(relativePath);
|
|
430
440
|
mediaTypes.push(mediaType);
|
|
@@ -460,7 +470,7 @@ export async function buildInboundContext(message, accountId, config, agentId, p
|
|
|
460
470
|
ChatType: message.chatType,
|
|
461
471
|
ConversationLabel: fromLabel,
|
|
462
472
|
Provider: OPENCLAW_WORKCLAW_CHANNEL,
|
|
463
|
-
Surface:
|
|
473
|
+
Surface: 'openclaw-workclaw_dm',
|
|
464
474
|
MessageSid: message.messageId,
|
|
465
475
|
Timestamp: message.timestamp,
|
|
466
476
|
OriginatingChannel: OPENCLAW_WORKCLAW_CHANNEL,
|