@ynhcj/xiaoyi-channel 0.0.50-beta → 0.0.52-beta
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/index.js +42 -0
- package/dist/src/bot.js +3 -16
- package/dist/src/client.js +0 -3
- package/dist/src/cspl/call-api.d.ts +3 -0
- package/dist/src/cspl/call-api.js +79 -0
- package/dist/src/cspl/config.d.ts +19 -0
- package/dist/src/cspl/config.js +50 -0
- package/dist/src/cspl/constants.d.ts +43 -0
- package/dist/src/cspl/constants.js +22 -0
- package/dist/src/cspl/utils.d.ts +10 -0
- package/dist/src/cspl/utils.js +57 -0
- package/dist/src/formatter.js +3 -23
- package/dist/src/heartbeat.js +0 -4
- package/dist/src/reply-dispatcher.js +32 -0
- package/dist/src/steer-injector.d.ts +16 -0
- package/dist/src/steer-injector.js +74 -0
- package/dist/src/tools/calendar-tool.js +2 -37
- package/dist/src/tools/call-phone-tool.js +1 -42
- package/dist/src/tools/create-alarm-tool.js +3 -74
- package/dist/src/tools/delete-alarm-tool.js +3 -45
- package/dist/src/tools/image-reading-tool.js +0 -47
- package/dist/src/tools/location-tool.js +1 -32
- package/dist/src/tools/modify-alarm-tool.js +3 -77
- package/dist/src/tools/modify-note-tool.js +1 -34
- package/dist/src/tools/note-tool.js +2 -4
- package/dist/src/tools/search-alarm-tool.js +3 -61
- package/dist/src/tools/search-calendar-tool.js +2 -39
- package/dist/src/tools/search-contact-tool.js +0 -30
- package/dist/src/tools/search-file-tool.js +0 -33
- package/dist/src/tools/search-message-tool.js +0 -33
- package/dist/src/tools/search-note-tool.js +1 -26
- package/dist/src/tools/search-photo-gallery-tool.js +2 -31
- package/dist/src/tools/send-file-to-user-tool.js +0 -39
- package/dist/src/tools/send-message-tool.js +1 -39
- package/dist/src/tools/session-manager.js +0 -45
- package/dist/src/tools/upload-file-tool.js +0 -49
- package/dist/src/tools/upload-photo-tool.js +0 -42
- package/dist/src/tools/view-push-result-tool.js +0 -11
- package/dist/src/tools/xiaoyi-collection-tool.js +4 -82
- package/dist/src/tools/xiaoyi-gui-tool.js +0 -34
- package/dist/src/websocket.js +24 -10
- package/package.json +1 -1
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
// Session manager for XY tool context
|
|
2
2
|
// Stores active session contexts that tools can access
|
|
3
3
|
import { AsyncLocalStorage } from "async_hooks";
|
|
4
|
-
import { logger } from "../utils/logger.js";
|
|
5
4
|
import { configManager } from "../utils/config-manager.js";
|
|
6
5
|
import { getCurrentTaskId, getCurrentMessageId } from "../task-manager.js";
|
|
7
6
|
// Map of sessionKey -> SessionContextWithRef
|
|
@@ -13,19 +12,12 @@ const asyncLocalStorage = new AsyncLocalStorage();
|
|
|
13
12
|
* Should be called when starting to process a message.
|
|
14
13
|
*/
|
|
15
14
|
export function registerSession(sessionKey, context) {
|
|
16
|
-
logger.log(`[SESSION_MANAGER] 📝 Registering session: ${sessionKey}`);
|
|
17
|
-
logger.log(`[SESSION_MANAGER] - sessionId: ${context.sessionId}`);
|
|
18
|
-
logger.log(`[SESSION_MANAGER] - taskId: ${context.taskId}`);
|
|
19
|
-
logger.log(`[SESSION_MANAGER] - messageId: ${context.messageId}`);
|
|
20
|
-
logger.log(`[SESSION_MANAGER] - agentId: ${context.agentId}`);
|
|
21
|
-
logger.log(`[SESSION_MANAGER] - Active sessions before: ${activeSessions.size}`);
|
|
22
15
|
const existing = activeSessions.get(sessionKey);
|
|
23
16
|
if (existing) {
|
|
24
17
|
// 更新上下文,增加引用计数
|
|
25
18
|
existing.taskId = context.taskId;
|
|
26
19
|
existing.messageId = context.messageId;
|
|
27
20
|
existing.refCount++;
|
|
28
|
-
logger.log(`[SESSION_MANAGER] - Updated existing, refCount=${existing.refCount}`);
|
|
29
21
|
}
|
|
30
22
|
else {
|
|
31
23
|
// 新建
|
|
@@ -33,45 +25,30 @@ export function registerSession(sessionKey, context) {
|
|
|
33
25
|
...context,
|
|
34
26
|
refCount: 1,
|
|
35
27
|
});
|
|
36
|
-
logger.log(`[SESSION_MANAGER] - Created new, refCount=1`);
|
|
37
28
|
}
|
|
38
|
-
logger.log(`[SESSION_MANAGER] - Active sessions after: ${activeSessions.size}`);
|
|
39
|
-
logger.log(`[SESSION_MANAGER] - All session keys: [${Array.from(activeSessions.keys()).join(", ")}]`);
|
|
40
29
|
}
|
|
41
30
|
/**
|
|
42
31
|
* Unregister a session context.
|
|
43
32
|
* Should be called when message processing is complete.
|
|
44
33
|
*/
|
|
45
34
|
export function unregisterSession(sessionKey) {
|
|
46
|
-
logger.log(`[SESSION_MANAGER] 🗑️ Unregistering session: ${sessionKey}`);
|
|
47
|
-
logger.log(`[SESSION_MANAGER] - Active sessions before: ${activeSessions.size}`);
|
|
48
|
-
logger.log(`[SESSION_MANAGER] - Session existed: ${activeSessions.has(sessionKey)}`);
|
|
49
35
|
const existing = activeSessions.get(sessionKey);
|
|
50
36
|
if (!existing) {
|
|
51
|
-
logger.log(`[SESSION_MANAGER] - Session not found`);
|
|
52
37
|
return;
|
|
53
38
|
}
|
|
54
39
|
existing.refCount--;
|
|
55
|
-
logger.log(`[SESSION_MANAGER] - Decremented refCount: ${existing.refCount}`);
|
|
56
40
|
if (existing.refCount <= 0) {
|
|
57
41
|
activeSessions.delete(sessionKey);
|
|
58
42
|
configManager.clearSession(existing.sessionId);
|
|
59
|
-
logger.log(`[SESSION_MANAGER] - Deleted (refCount=0)`);
|
|
60
43
|
}
|
|
61
|
-
logger.log(`[SESSION_MANAGER] - Active sessions after: ${activeSessions.size}`);
|
|
62
|
-
logger.log(`[SESSION_MANAGER] - Remaining session keys: [${Array.from(activeSessions.keys()).join(", ")}]`);
|
|
63
44
|
}
|
|
64
45
|
/**
|
|
65
46
|
* Get session context by sessionKey.
|
|
66
47
|
* Returns null if session not found.
|
|
67
48
|
*/
|
|
68
49
|
export function getSessionContext(sessionKey) {
|
|
69
|
-
logger.log(`[SESSION_MANAGER] 🔍 Getting session by key: ${sessionKey}`);
|
|
70
|
-
logger.log(`[SESSION_MANAGER] - Active sessions: ${activeSessions.size}`);
|
|
71
50
|
const contextWithRef = activeSessions.get(sessionKey) ?? null;
|
|
72
|
-
logger.log(`[SESSION_MANAGER] - Found: ${contextWithRef !== null}`);
|
|
73
51
|
if (contextWithRef) {
|
|
74
|
-
logger.log(`[SESSION_MANAGER] - sessionId: ${contextWithRef.sessionId}`);
|
|
75
52
|
// 返回时去掉refCount字段
|
|
76
53
|
const { refCount, ...context } = contextWithRef;
|
|
77
54
|
return context;
|
|
@@ -85,20 +62,12 @@ export function getSessionContext(sessionKey) {
|
|
|
85
62
|
* Returns null if no sessions are active.
|
|
86
63
|
*/
|
|
87
64
|
export function getLatestSessionContext() {
|
|
88
|
-
logger.log(`[SESSION_MANAGER] 🔍 Getting latest session context`);
|
|
89
|
-
logger.log(`[SESSION_MANAGER] - Active sessions count: ${activeSessions.size}`);
|
|
90
|
-
logger.log(`[SESSION_MANAGER] - Active session keys: [${Array.from(activeSessions.keys()).join(", ")}]`);
|
|
91
65
|
if (activeSessions.size === 0) {
|
|
92
|
-
logger.error(`[SESSION_MANAGER] - ❌ No active sessions found!`);
|
|
93
66
|
return null;
|
|
94
67
|
}
|
|
95
68
|
// Return the last added session
|
|
96
69
|
const sessions = Array.from(activeSessions.values());
|
|
97
70
|
const latestSessionWithRef = sessions[sessions.length - 1];
|
|
98
|
-
logger.log(`[SESSION_MANAGER] - ✅ Found latest session:`);
|
|
99
|
-
logger.log(`[SESSION_MANAGER] - sessionId: ${latestSessionWithRef.sessionId}`);
|
|
100
|
-
logger.log(`[SESSION_MANAGER] - taskId: ${latestSessionWithRef.taskId}`);
|
|
101
|
-
logger.log(`[SESSION_MANAGER] - messageId: ${latestSessionWithRef.messageId}`);
|
|
102
71
|
// 返回时去掉refCount字段
|
|
103
72
|
const { refCount, ...latestSession } = latestSessionWithRef;
|
|
104
73
|
return latestSession;
|
|
@@ -108,9 +77,6 @@ export function getLatestSessionContext() {
|
|
|
108
77
|
* This ensures thread-safe context isolation for concurrent requests.
|
|
109
78
|
*/
|
|
110
79
|
export function runWithSessionContext(context, callback) {
|
|
111
|
-
logger.log(`[SESSION_MANAGER] 🔐 Running with AsyncLocalStorage context`);
|
|
112
|
-
logger.log(`[SESSION_MANAGER] - sessionId: ${context.sessionId}`);
|
|
113
|
-
logger.log(`[SESSION_MANAGER] - taskId: ${context.taskId}`);
|
|
114
80
|
return asyncLocalStorage.run(context, callback);
|
|
115
81
|
}
|
|
116
82
|
/**
|
|
@@ -125,7 +91,6 @@ export function getCurrentSessionContext() {
|
|
|
125
91
|
// 1. Get base context from AsyncLocalStorage
|
|
126
92
|
const context = asyncLocalStorage.getStore() ?? null;
|
|
127
93
|
if (!context) {
|
|
128
|
-
logger.warn(`[SESSION_MANAGER] ⚠️ No session context in AsyncLocalStorage`);
|
|
129
94
|
return null;
|
|
130
95
|
}
|
|
131
96
|
// 2. Get latest taskId and messageId from task-manager
|
|
@@ -133,12 +98,6 @@ export function getCurrentSessionContext() {
|
|
|
133
98
|
const latestMessageId = getCurrentMessageId(context.sessionId);
|
|
134
99
|
// 3. If task-manager has a newer taskId, use the latest value
|
|
135
100
|
if (latestTaskId && latestTaskId !== context.taskId) {
|
|
136
|
-
logger.log(`[SESSION_MANAGER] 🔄 TaskId updated (interruption detected)`);
|
|
137
|
-
logger.log(`[SESSION_MANAGER] - sessionId: ${context.sessionId}`);
|
|
138
|
-
logger.log(`[SESSION_MANAGER] - Old taskId: ${context.taskId}`);
|
|
139
|
-
logger.log(`[SESSION_MANAGER] - New taskId: ${latestTaskId}`);
|
|
140
|
-
logger.log(`[SESSION_MANAGER] - Old messageId: ${context.messageId}`);
|
|
141
|
-
logger.log(`[SESSION_MANAGER] - New messageId: ${latestMessageId ?? context.messageId}`);
|
|
142
101
|
// Return updated context (create new object, don't modify original)
|
|
143
102
|
return {
|
|
144
103
|
...context,
|
|
@@ -147,9 +106,5 @@ export function getCurrentSessionContext() {
|
|
|
147
106
|
};
|
|
148
107
|
}
|
|
149
108
|
// 4. No update needed, return original context
|
|
150
|
-
logger.log(`[SESSION_MANAGER] ✅ Got current session context from AsyncLocalStorage`);
|
|
151
|
-
logger.log(`[SESSION_MANAGER] - sessionId: ${context.sessionId}`);
|
|
152
|
-
logger.log(`[SESSION_MANAGER] - taskId: ${context.taskId}`);
|
|
153
|
-
logger.log(`[SESSION_MANAGER] - messageId: ${context.messageId}`);
|
|
154
109
|
return context;
|
|
155
110
|
}
|
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
import { getXYWebSocketManager } from "../client.js";
|
|
2
2
|
import { sendCommand } from "../formatter.js";
|
|
3
3
|
import { getCurrentSessionContext } from "./session-manager.js";
|
|
4
|
-
import { logger } from "../utils/logger.js";
|
|
5
4
|
/**
|
|
6
5
|
* XY upload file tool - uploads local files to get publicly accessible URLs.
|
|
7
6
|
* Requires file URIs from search_file tool as prerequisite.
|
|
@@ -41,66 +40,49 @@ export const uploadFileTool = {
|
|
|
41
40
|
required: ["fileInfos"],
|
|
42
41
|
},
|
|
43
42
|
async execute(toolCallId, params) {
|
|
44
|
-
logger.log(`[UPLOAD_FILE_TOOL] 🚀 Starting execution`);
|
|
45
|
-
logger.log(`[UPLOAD_FILE_TOOL] - toolCallId: ${toolCallId}`);
|
|
46
|
-
logger.log(`[UPLOAD_FILE_TOOL] - params (raw):`, JSON.stringify(params));
|
|
47
|
-
logger.log(`[UPLOAD_FILE_TOOL] - params.fileInfos type:`, typeof params.fileInfos);
|
|
48
|
-
logger.log(`[UPLOAD_FILE_TOOL] - timestamp: ${new Date().toISOString()}`);
|
|
49
43
|
// ===== 参数规范化:兼容数组和 JSON 字符串 =====
|
|
50
44
|
let fileInfos = null;
|
|
51
45
|
if (!params.fileInfos) {
|
|
52
|
-
logger.error(`[UPLOAD_FILE_TOOL] ❌ Missing parameter: fileInfos`);
|
|
53
46
|
throw new Error("Missing required parameter: fileInfos");
|
|
54
47
|
}
|
|
55
48
|
// 情况1: 已经是数组
|
|
56
49
|
if (Array.isArray(params.fileInfos)) {
|
|
57
|
-
logger.log(`[UPLOAD_FILE_TOOL] ✅ fileInfos is already an array`);
|
|
58
50
|
fileInfos = params.fileInfos;
|
|
59
51
|
}
|
|
60
52
|
// 情况2: 是字符串,尝试解析为 JSON 数组
|
|
61
53
|
else if (typeof params.fileInfos === 'string') {
|
|
62
|
-
logger.log(`[UPLOAD_FILE_TOOL] 🔄 fileInfos is a string, attempting to parse as JSON...`);
|
|
63
54
|
try {
|
|
64
55
|
const parsed = JSON.parse(params.fileInfos);
|
|
65
56
|
if (Array.isArray(parsed)) {
|
|
66
|
-
logger.log(`[UPLOAD_FILE_TOOL] ✅ Successfully parsed JSON string to array`);
|
|
67
57
|
fileInfos = parsed;
|
|
68
58
|
}
|
|
69
59
|
else {
|
|
70
|
-
logger.error(`[UPLOAD_FILE_TOOL] ❌ Parsed JSON is not an array:`, typeof parsed);
|
|
71
60
|
throw new Error("fileInfos must be an array or a JSON string representing an array");
|
|
72
61
|
}
|
|
73
62
|
}
|
|
74
63
|
catch (parseError) {
|
|
75
|
-
logger.error(`[UPLOAD_FILE_TOOL] ❌ Failed to parse fileInfos as JSON:`, parseError);
|
|
76
64
|
throw new Error(`fileInfos must be a valid JSON array string. Parse error: ${parseError instanceof Error ? parseError.message : String(parseError)}`);
|
|
77
65
|
}
|
|
78
66
|
}
|
|
79
67
|
// 情况3: 其他类型,报错
|
|
80
68
|
else {
|
|
81
|
-
logger.error(`[UPLOAD_FILE_TOOL] ❌ Invalid fileInfos type:`, typeof params.fileInfos);
|
|
82
69
|
throw new Error(`fileInfos must be an array or a JSON string, got ${typeof params.fileInfos}`);
|
|
83
70
|
}
|
|
84
71
|
// 验证数组非空
|
|
85
72
|
if (!fileInfos || fileInfos.length === 0) {
|
|
86
|
-
logger.error(`[UPLOAD_FILE_TOOL] ❌ fileInfos array is empty`);
|
|
87
73
|
throw new Error("fileInfos array cannot be empty");
|
|
88
74
|
}
|
|
89
|
-
logger.log(`[UPLOAD_FILE_TOOL] ✅ Normalized fileInfos:`, JSON.stringify(fileInfos));
|
|
90
75
|
// Validate maximum 5 file infos
|
|
91
76
|
if (fileInfos.length > 5) {
|
|
92
|
-
logger.error(`[UPLOAD_FILE_TOOL] ❌ Too many fileInfos: ${fileInfos.length}`);
|
|
93
77
|
throw new Error(`最多支持 5 条文件信息,当前提供了 ${fileInfos.length} 条。请分批处理。`);
|
|
94
78
|
}
|
|
95
79
|
// Validate each fileInfo has required mediaUri field
|
|
96
80
|
for (let i = 0; i < fileInfos.length; i++) {
|
|
97
81
|
const fileInfo = fileInfos[i];
|
|
98
82
|
if (!fileInfo || typeof fileInfo !== 'object') {
|
|
99
|
-
logger.error(`[UPLOAD_FILE_TOOL] ❌ fileInfo at index ${i} is not an object`);
|
|
100
83
|
throw new Error(`fileInfos[${i}] must be an object with mediaUri property`);
|
|
101
84
|
}
|
|
102
85
|
if (!fileInfo.mediaUri || typeof fileInfo.mediaUri !== 'string') {
|
|
103
|
-
logger.error(`[UPLOAD_FILE_TOOL] ❌ fileInfo at index ${i} missing or invalid mediaUri`);
|
|
104
86
|
throw new Error(`fileInfos[${i}] must have a valid mediaUri string property`);
|
|
105
87
|
}
|
|
106
88
|
// Set default timeout if not provided
|
|
@@ -108,28 +90,16 @@ export const uploadFileTool = {
|
|
|
108
90
|
fileInfo.timeout = "20000";
|
|
109
91
|
}
|
|
110
92
|
}
|
|
111
|
-
logger.log(`[UPLOAD_FILE_TOOL] - fileInfos count: ${fileInfos.length}`);
|
|
112
93
|
// Get session context
|
|
113
|
-
logger.log(`[UPLOAD_FILE_TOOL] 🔍 Attempting to get session context...`);
|
|
114
94
|
const sessionContext = getCurrentSessionContext();
|
|
115
95
|
if (!sessionContext) {
|
|
116
|
-
logger.error(`[UPLOAD_FILE_TOOL] ❌ FAILED: No active session found!`);
|
|
117
|
-
logger.error(`[UPLOAD_FILE_TOOL] - toolCallId: ${toolCallId}`);
|
|
118
96
|
throw new Error("No active XY session found. Upload file tool can only be used during an active conversation.");
|
|
119
97
|
}
|
|
120
|
-
logger.log(`[UPLOAD_FILE_TOOL] ✅ Session context found`);
|
|
121
|
-
logger.log(`[UPLOAD_FILE_TOOL] - sessionId: ${sessionContext.sessionId}`);
|
|
122
|
-
logger.log(`[UPLOAD_FILE_TOOL] - taskId: ${sessionContext.taskId}`);
|
|
123
|
-
logger.log(`[UPLOAD_FILE_TOOL] - messageId: ${sessionContext.messageId}`);
|
|
124
98
|
const { config, sessionId, taskId, messageId } = sessionContext;
|
|
125
99
|
// Get WebSocket manager
|
|
126
|
-
logger.log(`[UPLOAD_FILE_TOOL] 🔌 Getting WebSocket manager...`);
|
|
127
100
|
const wsManager = getXYWebSocketManager(config);
|
|
128
|
-
logger.log(`[UPLOAD_FILE_TOOL] ✅ WebSocket manager obtained`);
|
|
129
101
|
// Get public URLs for the files
|
|
130
|
-
logger.log(`[UPLOAD_FILE_TOOL] 🌐 Getting public URLs for files...`);
|
|
131
102
|
const fileUrls = await getFileUrls(wsManager, config, sessionId, taskId, messageId, fileInfos);
|
|
132
|
-
logger.log(`[UPLOAD_FILE_TOOL] 🎉 Successfully retrieved ${fileUrls.length} file URLs`);
|
|
133
103
|
return {
|
|
134
104
|
content: [
|
|
135
105
|
{
|
|
@@ -149,7 +119,6 @@ export const uploadFileTool = {
|
|
|
149
119
|
* Returns array of publicly accessible file URLs
|
|
150
120
|
*/
|
|
151
121
|
async function getFileUrls(wsManager, config, sessionId, taskId, messageId, fileInfos) {
|
|
152
|
-
logger.log(`[UPLOAD_FILE_TOOL] 📦 Building FileUploadForClaw command...`);
|
|
153
122
|
const command = {
|
|
154
123
|
header: {
|
|
155
124
|
namespace: "Common",
|
|
@@ -185,26 +154,18 @@ async function getFileUrls(wsManager, config, sessionId, taskId, messageId, file
|
|
|
185
154
|
};
|
|
186
155
|
return new Promise((resolve, reject) => {
|
|
187
156
|
const timeout = setTimeout(() => {
|
|
188
|
-
logger.error(`[UPLOAD_FILE_TOOL] ⏰ Timeout: No response for FileUploadForClaw within 60 seconds`);
|
|
189
157
|
wsManager.off("data-event", handler);
|
|
190
158
|
reject(new Error("获取文件URL超时(60秒)"));
|
|
191
159
|
}, 60000);
|
|
192
160
|
const handler = (event) => {
|
|
193
|
-
logger.log(`[UPLOAD_FILE_TOOL] 📨 Received data event:`, JSON.stringify(event));
|
|
194
161
|
if (event.intentName === "FileUploadForClaw") {
|
|
195
|
-
logger.log(`[UPLOAD_FILE_TOOL] 🎯 FileUploadForClaw event received`);
|
|
196
|
-
logger.log(`[UPLOAD_FILE_TOOL] - status: ${event.status}`);
|
|
197
162
|
clearTimeout(timeout);
|
|
198
163
|
wsManager.off("data-event", handler);
|
|
199
164
|
if (event.status === "success" && event.outputs) {
|
|
200
|
-
logger.log(`[UPLOAD_FILE_TOOL] ✅ File URL retrieval completed successfully`);
|
|
201
165
|
// Check for error code in outputs
|
|
202
166
|
const code = event.outputs.code !== undefined ? event.outputs.code : null;
|
|
203
167
|
if (code !== null && code !== 0) {
|
|
204
|
-
logger.error(`[UPLOAD_FILE_TOOL] ❌ Device returned error`);
|
|
205
|
-
logger.error(`[UPLOAD_FILE_TOOL] - code: ${code}`);
|
|
206
168
|
const errorMsg = event.outputs.errorMsg || event.outputs.errMsg || "未知错误";
|
|
207
|
-
logger.error(`[UPLOAD_FILE_TOOL] - errorMsg: ${errorMsg}`);
|
|
208
169
|
reject(new Error(`获取文件URL失败: ${errorMsg} (错误代码: ${code})`));
|
|
209
170
|
return;
|
|
210
171
|
}
|
|
@@ -222,29 +183,21 @@ async function getFileUrls(wsManager, config, sessionId, taskId, messageId, file
|
|
|
222
183
|
// Replace \u003d with = and \u0026 with &
|
|
223
184
|
fileUrls = fileUrls.map((url) => {
|
|
224
185
|
if (typeof url !== 'string') {
|
|
225
|
-
logger.warn(`[UPLOAD_FILE_TOOL] ⚠️ URL is not a string:`, typeof url);
|
|
226
186
|
return '';
|
|
227
187
|
}
|
|
228
188
|
const decodedUrl = url
|
|
229
189
|
.replace(/\\u003d/g, '=')
|
|
230
190
|
.replace(/\\u0026/g, '&');
|
|
231
|
-
logger.log(`[UPLOAD_FILE_TOOL] 🔄 Decoded URL: ${url} -> ${decodedUrl}`);
|
|
232
191
|
return decodedUrl;
|
|
233
192
|
}).filter((url) => url.length > 0);
|
|
234
|
-
logger.log(`[UPLOAD_FILE_TOOL] 📊 Retrieved and decoded ${fileUrls.length} file URLs`);
|
|
235
193
|
resolve(fileUrls);
|
|
236
194
|
}
|
|
237
195
|
else {
|
|
238
|
-
logger.error(`[UPLOAD_FILE_TOOL] ❌ File URL retrieval failed`);
|
|
239
|
-
logger.error(`[UPLOAD_FILE_TOOL] - status: ${event.status}`);
|
|
240
|
-
logger.error(`[UPLOAD_FILE_TOOL] - outputs:`, JSON.stringify(event.outputs || {}));
|
|
241
196
|
reject(new Error(`获取文件URL失败: ${event.status}`));
|
|
242
197
|
}
|
|
243
198
|
}
|
|
244
199
|
};
|
|
245
|
-
logger.log(`[UPLOAD_FILE_TOOL] 📡 Registering data-event handler for FileUploadForClaw`);
|
|
246
200
|
wsManager.on("data-event", handler);
|
|
247
|
-
logger.log(`[UPLOAD_FILE_TOOL] 📤 Sending FileUploadForClaw command...`);
|
|
248
201
|
sendCommand({
|
|
249
202
|
config,
|
|
250
203
|
sessionId,
|
|
@@ -253,10 +206,8 @@ async function getFileUrls(wsManager, config, sessionId, taskId, messageId, file
|
|
|
253
206
|
command,
|
|
254
207
|
})
|
|
255
208
|
.then(() => {
|
|
256
|
-
logger.log(`[UPLOAD_FILE_TOOL] ✅ FileUploadForClaw command sent successfully`);
|
|
257
209
|
})
|
|
258
210
|
.catch((error) => {
|
|
259
|
-
logger.error(`[UPLOAD_FILE_TOOL] ❌ Failed to send FileUploadForClaw command:`, error);
|
|
260
211
|
clearTimeout(timeout);
|
|
261
212
|
wsManager.off("data-event", handler);
|
|
262
213
|
reject(error);
|
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
import { getXYWebSocketManager } from "../client.js";
|
|
2
2
|
import { sendCommand } from "../formatter.js";
|
|
3
3
|
import { getCurrentSessionContext } from "./session-manager.js";
|
|
4
|
-
import { logger } from "../utils/logger.js";
|
|
5
4
|
/**
|
|
6
5
|
* XY upload photo tool - uploads local photos to get publicly accessible URLs.
|
|
7
6
|
* Requires mediaUris from search_photo_gallery tool as prerequisite.
|
|
@@ -36,79 +35,52 @@ export const uploadPhotoTool = {
|
|
|
36
35
|
required: ["mediaUris"],
|
|
37
36
|
},
|
|
38
37
|
async execute(toolCallId, params) {
|
|
39
|
-
logger.log(`[UPLOAD_PHOTO_TOOL] 🚀 Starting execution`);
|
|
40
|
-
logger.log(`[UPLOAD_PHOTO_TOOL] - toolCallId: ${toolCallId}`);
|
|
41
|
-
logger.log(`[UPLOAD_PHOTO_TOOL] - params (raw):`, JSON.stringify(params));
|
|
42
|
-
logger.log(`[UPLOAD_PHOTO_TOOL] - params.mediaUris type:`, typeof params.mediaUris);
|
|
43
|
-
logger.log(`[UPLOAD_PHOTO_TOOL] - timestamp: ${new Date().toISOString()}`);
|
|
44
38
|
// ===== 参数规范化:兼容数组和 JSON 字符串 =====
|
|
45
39
|
let mediaUris = null;
|
|
46
40
|
if (!params.mediaUris) {
|
|
47
|
-
logger.error(`[UPLOAD_PHOTO_TOOL] ❌ Missing parameter: mediaUris`);
|
|
48
41
|
throw new Error("Missing required parameter: mediaUris");
|
|
49
42
|
}
|
|
50
43
|
// 情况1: 已经是数组
|
|
51
44
|
if (Array.isArray(params.mediaUris)) {
|
|
52
|
-
logger.log(`[UPLOAD_PHOTO_TOOL] ✅ mediaUris is already an array`);
|
|
53
45
|
mediaUris = params.mediaUris;
|
|
54
46
|
}
|
|
55
47
|
// 情况2: 是字符串,尝试解析为 JSON 数组
|
|
56
48
|
else if (typeof params.mediaUris === 'string') {
|
|
57
|
-
logger.log(`[UPLOAD_PHOTO_TOOL] 🔄 mediaUris is a string, attempting to parse as JSON...`);
|
|
58
49
|
try {
|
|
59
50
|
const parsed = JSON.parse(params.mediaUris);
|
|
60
51
|
if (Array.isArray(parsed)) {
|
|
61
|
-
logger.log(`[UPLOAD_PHOTO_TOOL] ✅ Successfully parsed JSON string to array`);
|
|
62
52
|
mediaUris = parsed;
|
|
63
53
|
}
|
|
64
54
|
else {
|
|
65
|
-
logger.error(`[UPLOAD_PHOTO_TOOL] ❌ Parsed JSON is not an array:`, typeof parsed);
|
|
66
55
|
throw new Error("mediaUris must be an array or a JSON string representing an array");
|
|
67
56
|
}
|
|
68
57
|
}
|
|
69
58
|
catch (parseError) {
|
|
70
|
-
logger.error(`[UPLOAD_PHOTO_TOOL] ❌ Failed to parse mediaUris as JSON:`, parseError);
|
|
71
59
|
throw new Error(`mediaUris must be a valid JSON array string. Parse error: ${parseError instanceof Error ? parseError.message : String(parseError)}`);
|
|
72
60
|
}
|
|
73
61
|
}
|
|
74
62
|
// 情况3: 其他类型,报错
|
|
75
63
|
else {
|
|
76
|
-
logger.error(`[UPLOAD_PHOTO_TOOL] ❌ Invalid mediaUris type:`, typeof params.mediaUris);
|
|
77
64
|
throw new Error(`mediaUris must be an array or a JSON string, got ${typeof params.mediaUris}`);
|
|
78
65
|
}
|
|
79
66
|
// 验证数组非空
|
|
80
67
|
if (!mediaUris || mediaUris.length === 0) {
|
|
81
|
-
logger.error(`[UPLOAD_PHOTO_TOOL] ❌ mediaUris array is empty`);
|
|
82
68
|
throw new Error("mediaUris array cannot be empty");
|
|
83
69
|
}
|
|
84
|
-
logger.log(`[UPLOAD_PHOTO_TOOL] ✅ Normalized mediaUris:`, JSON.stringify(mediaUris));
|
|
85
70
|
// Validate maximum 5 URIs
|
|
86
71
|
if (mediaUris.length > 5) {
|
|
87
|
-
logger.error(`[UPLOAD_PHOTO_TOOL] ❌ Too many mediaUris: ${mediaUris.length}`);
|
|
88
72
|
throw new Error(`最多支持 5 条 mediaUri,当前提供了 ${mediaUris.length} 条。请分批处理。`);
|
|
89
73
|
}
|
|
90
|
-
logger.log(`[UPLOAD_PHOTO_TOOL] - mediaUris count: ${mediaUris.length}`);
|
|
91
74
|
// Get session context
|
|
92
|
-
logger.log(`[UPLOAD_PHOTO_TOOL] 🔍 Attempting to get session context...`);
|
|
93
75
|
const sessionContext = getCurrentSessionContext();
|
|
94
76
|
if (!sessionContext) {
|
|
95
|
-
logger.error(`[UPLOAD_PHOTO_TOOL] ❌ FAILED: No active session found!`);
|
|
96
|
-
logger.error(`[UPLOAD_PHOTO_TOOL] - toolCallId: ${toolCallId}`);
|
|
97
77
|
throw new Error("No active XY session found. Upload photo tool can only be used during an active conversation.");
|
|
98
78
|
}
|
|
99
|
-
logger.log(`[UPLOAD_PHOTO_TOOL] ✅ Session context found`);
|
|
100
|
-
logger.log(`[UPLOAD_PHOTO_TOOL] - sessionId: ${sessionContext.sessionId}`);
|
|
101
|
-
logger.log(`[UPLOAD_PHOTO_TOOL] - taskId: ${sessionContext.taskId}`);
|
|
102
|
-
logger.log(`[UPLOAD_PHOTO_TOOL] - messageId: ${sessionContext.messageId}`);
|
|
103
79
|
const { config, sessionId, taskId, messageId } = sessionContext;
|
|
104
80
|
// Get WebSocket manager
|
|
105
|
-
logger.log(`[UPLOAD_PHOTO_TOOL] 🔌 Getting WebSocket manager...`);
|
|
106
81
|
const wsManager = getXYWebSocketManager(config);
|
|
107
|
-
logger.log(`[UPLOAD_PHOTO_TOOL] ✅ WebSocket manager obtained`);
|
|
108
82
|
// Get public URLs for the photos
|
|
109
|
-
logger.log(`[UPLOAD_PHOTO_TOOL] 🌐 Getting public URLs for photos...`);
|
|
110
83
|
const imageUrls = await getPhotoUrls(wsManager, config, sessionId, taskId, messageId, mediaUris);
|
|
111
|
-
logger.log(`[UPLOAD_PHOTO_TOOL] 🎉 Successfully retrieved ${imageUrls.length} photo URLs`);
|
|
112
84
|
return {
|
|
113
85
|
content: [
|
|
114
86
|
{
|
|
@@ -128,7 +100,6 @@ export const uploadPhotoTool = {
|
|
|
128
100
|
* Returns array of publicly accessible image URLs
|
|
129
101
|
*/
|
|
130
102
|
async function getPhotoUrls(wsManager, config, sessionId, taskId, messageId, mediaUris) {
|
|
131
|
-
logger.log(`[UPLOAD_PHOTO_TOOL] 📦 Building ImageUploadForClaw command...`);
|
|
132
103
|
// Build imageInfos array from mediaUris
|
|
133
104
|
const imageInfos = mediaUris.map(mediaUri => ({ mediaUri }));
|
|
134
105
|
const command = {
|
|
@@ -166,19 +137,14 @@ async function getPhotoUrls(wsManager, config, sessionId, taskId, messageId, med
|
|
|
166
137
|
};
|
|
167
138
|
return new Promise((resolve, reject) => {
|
|
168
139
|
const timeout = setTimeout(() => {
|
|
169
|
-
logger.error(`[UPLOAD_PHOTO_TOOL] ⏰ Timeout: No response for ImageUploadForClaw within 60 seconds`);
|
|
170
140
|
wsManager.off("data-event", handler);
|
|
171
141
|
reject(new Error("获取照片URL超时(60秒)"));
|
|
172
142
|
}, 60000);
|
|
173
143
|
const handler = (event) => {
|
|
174
|
-
logger.log(`[UPLOAD_PHOTO_TOOL] 📨 Received data event:`, JSON.stringify(event));
|
|
175
144
|
if (event.intentName === "ImageUploadForClaw") {
|
|
176
|
-
logger.log(`[UPLOAD_PHOTO_TOOL] 🎯 ImageUploadForClaw event received`);
|
|
177
|
-
logger.log(`[UPLOAD_PHOTO_TOOL] - status: ${event.status}`);
|
|
178
145
|
clearTimeout(timeout);
|
|
179
146
|
wsManager.off("data-event", handler);
|
|
180
147
|
if (event.status === "success" && event.outputs) {
|
|
181
|
-
logger.log(`[UPLOAD_PHOTO_TOOL] ✅ Image URL retrieval completed successfully`);
|
|
182
148
|
const result = event.outputs.result;
|
|
183
149
|
let imageUrls = result?.imageUrls || [];
|
|
184
150
|
// Decode Unicode escape sequences in URLs
|
|
@@ -187,22 +153,16 @@ async function getPhotoUrls(wsManager, config, sessionId, taskId, messageId, med
|
|
|
187
153
|
const decodedUrl = url
|
|
188
154
|
.replace(/\\u003d/g, '=')
|
|
189
155
|
.replace(/\\u0026/g, '&');
|
|
190
|
-
logger.log(`[UPLOAD_PHOTO_TOOL] 🔄 Decoded URL: ${url} -> ${decodedUrl}`);
|
|
191
156
|
return decodedUrl;
|
|
192
157
|
});
|
|
193
|
-
logger.log(`[UPLOAD_PHOTO_TOOL] 📊 Retrieved and decoded ${imageUrls.length} image URLs`);
|
|
194
158
|
resolve(imageUrls);
|
|
195
159
|
}
|
|
196
160
|
else {
|
|
197
|
-
logger.error(`[UPLOAD_PHOTO_TOOL] ❌ Image URL retrieval failed`);
|
|
198
|
-
logger.error(`[UPLOAD_PHOTO_TOOL] - status: ${event.status}`);
|
|
199
161
|
reject(new Error(`获取照片URL失败: ${event.status}`));
|
|
200
162
|
}
|
|
201
163
|
}
|
|
202
164
|
};
|
|
203
|
-
logger.log(`[UPLOAD_PHOTO_TOOL] 📡 Registering data-event handler for ImageUploadForClaw`);
|
|
204
165
|
wsManager.on("data-event", handler);
|
|
205
|
-
logger.log(`[UPLOAD_PHOTO_TOOL] 📤 Sending ImageUploadForClaw command...`);
|
|
206
166
|
sendCommand({
|
|
207
167
|
config,
|
|
208
168
|
sessionId,
|
|
@@ -211,10 +171,8 @@ async function getPhotoUrls(wsManager, config, sessionId, taskId, messageId, med
|
|
|
211
171
|
command,
|
|
212
172
|
})
|
|
213
173
|
.then(() => {
|
|
214
|
-
logger.log(`[UPLOAD_PHOTO_TOOL] ✅ ImageUploadForClaw command sent successfully`);
|
|
215
174
|
})
|
|
216
175
|
.catch((error) => {
|
|
217
|
-
logger.error(`[UPLOAD_PHOTO_TOOL] ❌ Failed to send ImageUploadForClaw command:`, error);
|
|
218
176
|
clearTimeout(timeout);
|
|
219
177
|
wsManager.off("data-event", handler);
|
|
220
178
|
reject(error);
|
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
import { searchPushData, getAllPushData } from "../utils/pushdata-manager.js";
|
|
2
|
-
import { logger } from "../utils/logger.js";
|
|
3
2
|
/**
|
|
4
3
|
* 查看推送任务执行结果工具
|
|
5
4
|
* 支持关键词搜索或查看最近的推送记录
|
|
@@ -35,26 +34,18 @@ export const viewPushResultTool = {
|
|
|
35
34
|
required: [],
|
|
36
35
|
},
|
|
37
36
|
async execute(toolCallId, params) {
|
|
38
|
-
logger.log(`[VIEW_PUSH_RESULT_TOOL] 🚀 Starting execution`);
|
|
39
|
-
logger.log(`[VIEW_PUSH_RESULT_TOOL] - toolCallId: ${toolCallId}`);
|
|
40
|
-
logger.log(`[VIEW_PUSH_RESULT_TOOL] - params:`, JSON.stringify(params));
|
|
41
37
|
const keywords = params.keywords?.trim();
|
|
42
38
|
const limit = Math.min(params.limit || 10, 50); // 限制最多50条
|
|
43
39
|
try {
|
|
44
|
-
logger.log(`[VIEW_PUSH_RESULT_TOOL] 🔍 Searching push data...`);
|
|
45
|
-
logger.log(`[VIEW_PUSH_RESULT_TOOL] - keywords: ${keywords || '(none)'}`);
|
|
46
|
-
logger.log(`[VIEW_PUSH_RESULT_TOOL] - limit: ${limit}`);
|
|
47
40
|
// 根据是否有关键词决定调用哪个方法
|
|
48
41
|
let results = keywords
|
|
49
42
|
? await searchPushData(keywords)
|
|
50
43
|
: await getAllPushData();
|
|
51
|
-
logger.log(`[VIEW_PUSH_RESULT_TOOL] Found ${results.length} items before limit`);
|
|
52
44
|
// 按时间倒序排序(最新的在前)
|
|
53
45
|
results.sort((a, b) => b.time.localeCompare(a.time));
|
|
54
46
|
// 限制返回条数
|
|
55
47
|
results = results.slice(0, limit);
|
|
56
48
|
if (results.length === 0) {
|
|
57
|
-
logger.log(`[VIEW_PUSH_RESULT_TOOL] ⚠️ No results found`);
|
|
58
49
|
return {
|
|
59
50
|
content: [
|
|
60
51
|
{
|
|
@@ -81,7 +72,6 @@ export const viewPushResultTool = {
|
|
|
81
72
|
: item.dataDetail,
|
|
82
73
|
fullLength: item.dataDetail.length,
|
|
83
74
|
}));
|
|
84
|
-
logger.log(`[VIEW_PUSH_RESULT_TOOL] ✅ Returning ${formattedItems.length} items`);
|
|
85
75
|
return {
|
|
86
76
|
content: [
|
|
87
77
|
{
|
|
@@ -100,7 +90,6 @@ export const viewPushResultTool = {
|
|
|
100
90
|
};
|
|
101
91
|
}
|
|
102
92
|
catch (error) {
|
|
103
|
-
logger.error(`[VIEW_PUSH_RESULT_TOOL] ❌ Failed to execute:`, error);
|
|
104
93
|
return {
|
|
105
94
|
content: [
|
|
106
95
|
{
|
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
import { getXYWebSocketManager } from "../client.js";
|
|
2
2
|
import { sendCommand } from "../formatter.js";
|
|
3
3
|
import { getCurrentSessionContext } from "./session-manager.js";
|
|
4
|
-
import { logger } from "../utils/logger.js";
|
|
5
4
|
/**
|
|
6
5
|
* XY collection tool - retrieves user's collection data from XiaoYi.
|
|
7
6
|
* Returns personalized knowledge data saved in user's collection.
|
|
@@ -22,29 +21,15 @@ export const xiaoyiCollectionTool = {
|
|
|
22
21
|
required: [],
|
|
23
22
|
},
|
|
24
23
|
async execute(toolCallId, params) {
|
|
25
|
-
logger.log(`[XIAOYI_COLLECTION_TOOL] 🚀 Starting execution`);
|
|
26
|
-
logger.log(`[XIAOYI_COLLECTION_TOOL] - toolCallId: ${toolCallId}`);
|
|
27
|
-
logger.log(`[XIAOYI_COLLECTION_TOOL] - params:`, JSON.stringify(params));
|
|
28
|
-
logger.log(`[XIAOYI_COLLECTION_TOOL] - timestamp: ${new Date().toISOString()}`);
|
|
29
24
|
// Get session context
|
|
30
|
-
logger.log(`[XIAOYI_COLLECTION_TOOL] 🔍 Attempting to get session context...`);
|
|
31
25
|
const sessionContext = getCurrentSessionContext();
|
|
32
26
|
if (!sessionContext) {
|
|
33
|
-
logger.error(`[XIAOYI_COLLECTION_TOOL] ❌ FAILED: No active session found!`);
|
|
34
|
-
logger.error(`[XIAOYI_COLLECTION_TOOL] - toolCallId: ${toolCallId}`);
|
|
35
27
|
throw new Error("No active XY session found. XiaoYi collection tool can only be used during an active conversation.");
|
|
36
28
|
}
|
|
37
|
-
logger.log(`[XIAOYI_COLLECTION_TOOL] ✅ Session context found`);
|
|
38
|
-
logger.log(`[XIAOYI_COLLECTION_TOOL] - sessionId: ${sessionContext.sessionId}`);
|
|
39
|
-
logger.log(`[XIAOYI_COLLECTION_TOOL] - taskId: ${sessionContext.taskId}`);
|
|
40
|
-
logger.log(`[XIAOYI_COLLECTION_TOOL] - messageId: ${sessionContext.messageId}`);
|
|
41
29
|
const { config, sessionId, taskId, messageId } = sessionContext;
|
|
42
30
|
// Get WebSocket manager
|
|
43
|
-
logger.log(`[XIAOYI_COLLECTION_TOOL] 🔌 Getting WebSocket manager...`);
|
|
44
31
|
const wsManager = getXYWebSocketManager(config);
|
|
45
|
-
logger.log(`[XIAOYI_COLLECTION_TOOL] ✅ WebSocket manager obtained`);
|
|
46
32
|
// Build QueryCollection command
|
|
47
|
-
logger.log(`[XIAOYI_COLLECTION_TOOL] 📦 Building QueryCollection command...`);
|
|
48
33
|
const command = {
|
|
49
34
|
header: {
|
|
50
35
|
namespace: "Common",
|
|
@@ -79,96 +64,35 @@ export const xiaoyiCollectionTool = {
|
|
|
79
64
|
},
|
|
80
65
|
};
|
|
81
66
|
// Send command and wait for response (60 second timeout)
|
|
82
|
-
logger.log(`[XIAOYI_COLLECTION_TOOL] ⏳ Setting up promise to wait for collection query response...`);
|
|
83
|
-
logger.log(`[XIAOYI_COLLECTION_TOOL] - Timeout: 60 seconds`);
|
|
84
67
|
return new Promise((resolve, reject) => {
|
|
85
68
|
const timeout = setTimeout(() => {
|
|
86
|
-
logger.error(`[XIAOYI_COLLECTION_TOOL] ⏰ Timeout: No response received within 60 seconds`);
|
|
87
69
|
wsManager.off("data-event", handler);
|
|
88
70
|
reject(new Error("查询小艺收藏超时(60秒)"));
|
|
89
71
|
}, 60000);
|
|
90
72
|
// Listen for data events from WebSocket
|
|
91
73
|
const handler = (event) => {
|
|
92
|
-
logger.log(`[XIAOYI_COLLECTION_TOOL] 📨 Received data event:`, JSON.stringify(event));
|
|
93
74
|
if (event.intentName === "QueryCollection") {
|
|
94
|
-
logger.log(`[XIAOYI_COLLECTION_TOOL] 🎯 QueryCollection event received`);
|
|
95
|
-
logger.log(`[XIAOYI_COLLECTION_TOOL] - status: ${event.status}`);
|
|
96
75
|
clearTimeout(timeout);
|
|
97
76
|
wsManager.off("data-event", handler);
|
|
98
77
|
if (event.status === "success" && event.outputs) {
|
|
99
|
-
|
|
100
|
-
logger.log(`[XIAOYI_COLLECTION_TOOL] - outputs:`, JSON.stringify(event.outputs));
|
|
101
|
-
// Check for error code first
|
|
102
|
-
if (event.outputs.code && event.outputs.code !== 0) {
|
|
103
|
-
logger.error(`[XIAOYI_COLLECTION_TOOL] ❌ Query failed with error code: ${event.outputs.code}`);
|
|
104
|
-
reject(new Error(`查询小艺收藏失败 (错误码: ${event.outputs.code})`));
|
|
105
|
-
return;
|
|
106
|
-
}
|
|
107
|
-
// Get the result from outputs
|
|
108
|
-
const result = event.outputs.result;
|
|
109
|
-
// Check if result exists
|
|
110
|
-
if (!result) {
|
|
111
|
-
logger.warn(`[XIAOYI_COLLECTION_TOOL] ⚠️ No collection data found`);
|
|
112
|
-
resolve({
|
|
113
|
-
content: [
|
|
114
|
-
{
|
|
115
|
-
type: "text",
|
|
116
|
-
text: JSON.stringify({
|
|
117
|
-
success: true,
|
|
118
|
-
memoryInfo: [],
|
|
119
|
-
message: "未找到收藏数据"
|
|
120
|
-
}),
|
|
121
|
-
},
|
|
122
|
-
],
|
|
123
|
-
});
|
|
124
|
-
return;
|
|
125
|
-
}
|
|
126
|
-
// Extract memoryInfo from the nested result structure
|
|
127
|
-
const memoryInfo = result.result?.memoryInfo || [];
|
|
128
|
-
logger.log(`[XIAOYI_COLLECTION_TOOL] 📊 Collections found: ${memoryInfo.length} items`);
|
|
129
|
-
// Process and simplify the collection data
|
|
130
|
-
const simplifiedCollections = memoryInfo.map((item) => ({
|
|
131
|
-
uuid: item.uuid,
|
|
132
|
-
type: item.type,
|
|
133
|
-
status: item.status,
|
|
134
|
-
collectionTime: item.collectionTime,
|
|
135
|
-
editTime: item.editTime,
|
|
136
|
-
title: item.linkTitle || item.aiTitle || item.textTitle || item.imageTitle || item.podcastTitle || "",
|
|
137
|
-
description: item.description || item.abstract || "",
|
|
138
|
-
content: item.textContent || "",
|
|
139
|
-
linkUrl: item.linkUrl,
|
|
140
|
-
linkType: item.linkType,
|
|
141
|
-
appName: item.appNameFromPab || item.appName || "",
|
|
142
|
-
labels: item.label || [],
|
|
143
|
-
collectionMethod: item.collectionMethod,
|
|
144
|
-
}));
|
|
145
|
-
// Return the result with valid string content
|
|
78
|
+
// 成功,直接返回完整的 event.outputs JSON 字符串
|
|
146
79
|
resolve({
|
|
147
80
|
content: [
|
|
148
81
|
{
|
|
149
82
|
type: "text",
|
|
150
|
-
text: JSON.stringify(
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
collections: simplifiedCollections,
|
|
154
|
-
message: result.message,
|
|
155
|
-
}),
|
|
156
|
-
},
|
|
157
|
-
],
|
|
83
|
+
text: JSON.stringify(event.outputs),
|
|
84
|
+
}
|
|
85
|
+
]
|
|
158
86
|
});
|
|
159
87
|
}
|
|
160
88
|
else {
|
|
161
|
-
logger.error(`[XIAOYI_COLLECTION_TOOL] ❌ Collection query failed`);
|
|
162
|
-
logger.error(`[XIAOYI_COLLECTION_TOOL] - status: ${event.status}`);
|
|
163
89
|
reject(new Error(`查询小艺收藏失败: ${event.status}`));
|
|
164
90
|
}
|
|
165
91
|
}
|
|
166
92
|
};
|
|
167
93
|
// Register event handler
|
|
168
|
-
logger.log(`[XIAOYI_COLLECTION_TOOL] 📡 Registering data-event handler on WebSocket manager`);
|
|
169
94
|
wsManager.on("data-event", handler);
|
|
170
95
|
// Send the command
|
|
171
|
-
logger.log(`[XIAOYI_COLLECTION_TOOL] 📤 Sending QueryCollection command...`);
|
|
172
96
|
sendCommand({
|
|
173
97
|
config,
|
|
174
98
|
sessionId,
|
|
@@ -177,10 +101,8 @@ export const xiaoyiCollectionTool = {
|
|
|
177
101
|
command,
|
|
178
102
|
})
|
|
179
103
|
.then(() => {
|
|
180
|
-
logger.log(`[XIAOYI_COLLECTION_TOOL] ✅ Command sent successfully, waiting for response...`);
|
|
181
104
|
})
|
|
182
105
|
.catch((error) => {
|
|
183
|
-
logger.error(`[XIAOYI_COLLECTION_TOOL] ❌ Failed to send command:`, error);
|
|
184
106
|
clearTimeout(timeout);
|
|
185
107
|
wsManager.off("data-event", handler);
|
|
186
108
|
reject(error);
|