@ynhcj/xiaoyi-channel 0.0.76-beta → 0.0.78-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/src/channel.js +2 -1
- package/dist/src/tools/save-file-to-phone-tool.d.ts +5 -0
- package/dist/src/tools/save-file-to-phone-tool.js +170 -0
- package/dist/src/tools/save-media-to-gallery-tool.js +1 -1
- package/dist/src/tools/xiaoyi-add-collection-tool.js +1 -1
- package/dist/src/tools/xiaoyi-collection-tool.js +1 -1
- package/dist/src/tools/xiaoyi-delete-collection-tool.js +1 -1
- package/package.json +1 -1
package/dist/src/channel.js
CHANGED
|
@@ -28,6 +28,7 @@ import { xiaoyiCollectionTool } from "./tools/xiaoyi-collection-tool.js";
|
|
|
28
28
|
import { xiaoyiAddCollectionTool } from "./tools/xiaoyi-add-collection-tool.js";
|
|
29
29
|
import { xiaoyiDeleteCollectionTool } from "./tools/xiaoyi-delete-collection-tool.js";
|
|
30
30
|
import { saveMediaToGalleryTool } from "./tools/save-media-to-gallery-tool.js";
|
|
31
|
+
import { saveFileToPhoneTool } from "./tools/save-file-to-phone-tool.js";
|
|
31
32
|
import { filterToolsByDevice } from "./tools/device-tool-map.js";
|
|
32
33
|
import { getCurrentSessionContext } from "./tools/session-manager.js";
|
|
33
34
|
import { logger } from "./utils/logger.js";
|
|
@@ -70,7 +71,7 @@ export const xyPlugin = {
|
|
|
70
71
|
},
|
|
71
72
|
outbound: xyOutbound,
|
|
72
73
|
agentTools: () => {
|
|
73
|
-
const allTools = [locationTool, noteTool, searchNoteTool, modifyNoteTool, calendarTool, searchCalendarTool, searchContactTool, searchPhotoGalleryTool, uploadPhotoTool, xiaoyiGuiTool, callPhoneTool, searchMessageTool, sendMessageTool, searchFileTool, uploadFileTool, createAlarmTool, searchAlarmTool, modifyAlarmTool, deleteAlarmTool, sendFileToUserTool, viewPushResultTool, imageReadingTool, timestampToUtc8Tool, xiaoyiCollectionTool, xiaoyiAddCollectionTool, xiaoyiDeleteCollectionTool, saveMediaToGalleryTool];
|
|
74
|
+
const allTools = [locationTool, noteTool, searchNoteTool, modifyNoteTool, calendarTool, searchCalendarTool, searchContactTool, searchPhotoGalleryTool, uploadPhotoTool, xiaoyiGuiTool, callPhoneTool, searchMessageTool, sendMessageTool, searchFileTool, uploadFileTool, createAlarmTool, searchAlarmTool, modifyAlarmTool, deleteAlarmTool, sendFileToUserTool, viewPushResultTool, imageReadingTool, timestampToUtc8Tool, xiaoyiCollectionTool, xiaoyiAddCollectionTool, xiaoyiDeleteCollectionTool, saveMediaToGalleryTool, saveFileToPhoneTool];
|
|
74
75
|
const ctx = getCurrentSessionContext();
|
|
75
76
|
const filtered = filterToolsByDevice(allTools, ctx?.deviceType);
|
|
76
77
|
logger.log(`[DEVICE-FILTER] deviceType=${ctx?.deviceType ?? "(none)"}, tools: ${allTools.length} → ${filtered.length} (${filtered.map(t => t.name).join(", ")})`);
|
|
@@ -0,0 +1,170 @@
|
|
|
1
|
+
import { getXYWebSocketManager } from "../client.js";
|
|
2
|
+
import { sendCommand } from "../formatter.js";
|
|
3
|
+
import { getCurrentSessionContext } from "./session-manager.js";
|
|
4
|
+
import { XYFileUploadService } from "../file-upload.js";
|
|
5
|
+
/**
|
|
6
|
+
* Duck-typed ToolInputError: openclaw 按 .name 字段匹配,不用 instanceof。
|
|
7
|
+
* 抛出此错误会让 openclaw 返回 HTTP 400 而非 500,
|
|
8
|
+
* LLM 会将其识别为参数错误而非瞬时故障,不会触发重试。
|
|
9
|
+
*/
|
|
10
|
+
class ToolInputError extends Error {
|
|
11
|
+
status = 400;
|
|
12
|
+
constructor(message) {
|
|
13
|
+
super(message);
|
|
14
|
+
this.name = "ToolInputError";
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
/**
|
|
18
|
+
* XY save file to phone tool - saves files to user's device file manager.
|
|
19
|
+
* Supports local file paths (auto-uploaded to get public URL) and public URLs.
|
|
20
|
+
*/
|
|
21
|
+
export const saveFileToPhoneTool = {
|
|
22
|
+
name: "save_file_to_file_manager",
|
|
23
|
+
label: "Save File to Phone",
|
|
24
|
+
description: `将文件保存到手机文件管理器。
|
|
25
|
+
工具参数说明:
|
|
26
|
+
a. fileName:必填,string类型,文件名称。
|
|
27
|
+
b. url:必填,string类型,支持本地路径或者公网url路径。如果是本地路径,会先上传获取公网url再保存到手机。
|
|
28
|
+
c. suffix:必填,string类型,文件后缀,例如 ppt、doc、pdf 等。
|
|
29
|
+
|
|
30
|
+
注意:
|
|
31
|
+
a. 操作超时时间为60秒,请勿重复调用此工具
|
|
32
|
+
b. 如果遇到各类调用失败场景,不可以重试,直接返回错误。
|
|
33
|
+
c. 调用工具前需认真检查调用参数是否满足工具要求
|
|
34
|
+
|
|
35
|
+
回复约束:如果工具返回没有授权或者其他报错,只需要完整描述没有授权或者其他报错内容即可,不需要主动给用户提供解决方案,例如告诉用户如何授权,如何解决报错等都是不需要的,请严格遵守。
|
|
36
|
+
`,
|
|
37
|
+
parameters: {
|
|
38
|
+
type: "object",
|
|
39
|
+
properties: {
|
|
40
|
+
fileName: {
|
|
41
|
+
type: "string",
|
|
42
|
+
description: "必填,文件名称。",
|
|
43
|
+
},
|
|
44
|
+
url: {
|
|
45
|
+
type: "string",
|
|
46
|
+
description: "必填,支持本地路径或者公网url路径。如果是本地路径会先上传获取公网url。",
|
|
47
|
+
},
|
|
48
|
+
suffix: {
|
|
49
|
+
type: "string",
|
|
50
|
+
description: "必填,文件后缀,例如 ppt、doc、pdf 等。",
|
|
51
|
+
},
|
|
52
|
+
},
|
|
53
|
+
required: ["fileName", "url", "suffix"],
|
|
54
|
+
},
|
|
55
|
+
async execute(toolCallId, params) {
|
|
56
|
+
// Validate parameters
|
|
57
|
+
const { fileName, url, suffix } = params;
|
|
58
|
+
if (!url || typeof url !== "string") {
|
|
59
|
+
throw new ToolInputError("缺少必填参数: url");
|
|
60
|
+
}
|
|
61
|
+
if (!fileName || typeof fileName !== "string") {
|
|
62
|
+
throw new ToolInputError("缺少必填参数: fileName");
|
|
63
|
+
}
|
|
64
|
+
if (!suffix || typeof suffix !== "string") {
|
|
65
|
+
throw new ToolInputError("缺少必填参数: suffix");
|
|
66
|
+
}
|
|
67
|
+
// Get session context
|
|
68
|
+
const sessionContext = getCurrentSessionContext();
|
|
69
|
+
if (!sessionContext) {
|
|
70
|
+
throw new Error("No active XY session found. SaveFileToFileManager tool can only be used during an active conversation.");
|
|
71
|
+
}
|
|
72
|
+
const { config, sessionId, taskId, messageId } = sessionContext;
|
|
73
|
+
// Get WebSocket manager
|
|
74
|
+
const wsManager = getXYWebSocketManager(config);
|
|
75
|
+
// Determine the URL: if it's a local path, upload first to get public URL
|
|
76
|
+
let publicUrl = url;
|
|
77
|
+
if (!url.startsWith("http://") && !url.startsWith("https://")) {
|
|
78
|
+
// Local file path - upload to get public URL
|
|
79
|
+
const uploadService = new XYFileUploadService(config.fileUploadUrl, config.apiKey, config.uid);
|
|
80
|
+
publicUrl = await uploadService.uploadFileAndGetUrl(url);
|
|
81
|
+
if (!publicUrl) {
|
|
82
|
+
throw new Error("本地文件上传失败,无法获取公网URL");
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
// Build intentParam
|
|
86
|
+
const intentParam = {
|
|
87
|
+
fileName: fileName,
|
|
88
|
+
url: publicUrl,
|
|
89
|
+
suffix: suffix,
|
|
90
|
+
};
|
|
91
|
+
// Build SaveFileToFileManager command
|
|
92
|
+
const command = {
|
|
93
|
+
header: {
|
|
94
|
+
namespace: "Common",
|
|
95
|
+
name: "Action",
|
|
96
|
+
},
|
|
97
|
+
payload: {
|
|
98
|
+
cardParam: {},
|
|
99
|
+
executeParam: {
|
|
100
|
+
executeMode: "background",
|
|
101
|
+
intentName: "SaveFileToFileManager",
|
|
102
|
+
bundleName: "com.huawei.hmos.vassistant",
|
|
103
|
+
dimension: "",
|
|
104
|
+
needUnlock: true,
|
|
105
|
+
actionResponse: true,
|
|
106
|
+
appType: "OHOS_APP",
|
|
107
|
+
timeOut: 5,
|
|
108
|
+
timeout: 55000,
|
|
109
|
+
intentParam,
|
|
110
|
+
permissionId: ["ohos.permission.WRITE_IMAGEVIDEO"],
|
|
111
|
+
achieveType: "INTENT",
|
|
112
|
+
},
|
|
113
|
+
responses: [
|
|
114
|
+
{
|
|
115
|
+
resultCode: "",
|
|
116
|
+
displayText: "",
|
|
117
|
+
ttsText: "",
|
|
118
|
+
},
|
|
119
|
+
],
|
|
120
|
+
needUploadResult: true,
|
|
121
|
+
noHalfPage: false,
|
|
122
|
+
pageControlRelated: false,
|
|
123
|
+
},
|
|
124
|
+
};
|
|
125
|
+
// Send command and wait for response (60 second timeout)
|
|
126
|
+
return new Promise((resolve, reject) => {
|
|
127
|
+
const timeout = setTimeout(() => {
|
|
128
|
+
wsManager.off("data-event", handler);
|
|
129
|
+
reject(new Error("保存文件到手机超时(60秒)"));
|
|
130
|
+
}, 60000);
|
|
131
|
+
// Listen for data events from WebSocket
|
|
132
|
+
const handler = (event) => {
|
|
133
|
+
if (event.intentName === "SaveFileToFileManager") {
|
|
134
|
+
clearTimeout(timeout);
|
|
135
|
+
wsManager.off("data-event", handler);
|
|
136
|
+
if (event.status === "success" && event.outputs) {
|
|
137
|
+
resolve({
|
|
138
|
+
content: [
|
|
139
|
+
{
|
|
140
|
+
type: "text",
|
|
141
|
+
text: JSON.stringify(event.outputs),
|
|
142
|
+
}
|
|
143
|
+
]
|
|
144
|
+
});
|
|
145
|
+
}
|
|
146
|
+
else {
|
|
147
|
+
reject(new Error(`保存文件到手机失败: ${event.status}`));
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
};
|
|
151
|
+
// Register event handler
|
|
152
|
+
wsManager.on("data-event", handler);
|
|
153
|
+
// Send the command
|
|
154
|
+
sendCommand({
|
|
155
|
+
config,
|
|
156
|
+
sessionId,
|
|
157
|
+
taskId,
|
|
158
|
+
messageId,
|
|
159
|
+
command,
|
|
160
|
+
})
|
|
161
|
+
.then(() => {
|
|
162
|
+
})
|
|
163
|
+
.catch((error) => {
|
|
164
|
+
clearTimeout(timeout);
|
|
165
|
+
wsManager.off("data-event", handler);
|
|
166
|
+
reject(error);
|
|
167
|
+
});
|
|
168
|
+
});
|
|
169
|
+
},
|
|
170
|
+
};
|
|
@@ -19,7 +19,7 @@ class ToolInputError extends Error {
|
|
|
19
19
|
* Supports local file paths (auto-uploaded to get public URL) and public URLs.
|
|
20
20
|
*/
|
|
21
21
|
export const saveMediaToGalleryTool = {
|
|
22
|
-
name: "
|
|
22
|
+
name: "save_media_to_gallery",
|
|
23
23
|
label: "Save Media to Gallery",
|
|
24
24
|
description: `将图片文件或者视频文件保存到手机图库。
|
|
25
25
|
工具参数说明:
|
|
@@ -18,7 +18,7 @@ class ToolInputError extends Error {
|
|
|
18
18
|
* XY add collection tool - adds data to user's XiaoYi collection.
|
|
19
19
|
*/
|
|
20
20
|
export const xiaoyiAddCollectionTool = {
|
|
21
|
-
name: "
|
|
21
|
+
name: "add_collection",
|
|
22
22
|
label: "Add XiaoYi Collection",
|
|
23
23
|
description: `向小艺收藏中添加公共知识数据,可以给用户提供个性化体验。任何用户希望保存到个人化知识库中的数据都可以调用本技能。不同类型的数据对应的数据要求如下:
|
|
24
24
|
请求入参说明:
|
|
@@ -18,7 +18,7 @@ class ToolInputError extends Error {
|
|
|
18
18
|
* Returns personalized knowledge data saved in user's collection.
|
|
19
19
|
*/
|
|
20
20
|
export const xiaoyiCollectionTool = {
|
|
21
|
-
name: "
|
|
21
|
+
name: "query_collection",
|
|
22
22
|
label: "XiaoYi Collection",
|
|
23
23
|
description: `检索用户在小艺收藏中记下来的公共知识数据,本技能支持查询用户收藏的公共知识数据,也可以根据特定语义化描述进行特定内容的检索,通过参数进行控制。本技能返回结果中,linkTitle是收藏内容的标题,description是对收藏内容的总结,label是收藏内容的标签,linkUrl是可以直接访问的原始内容链接。如果你认为某条数据对用户交互有用,可以通过linkUrl抓取更加丰富的原始数据。
|
|
24
24
|
注意:
|
|
@@ -17,7 +17,7 @@ class ToolInputError extends Error {
|
|
|
17
17
|
* XY delete collection tool - deletes data from user's XiaoYi collection.
|
|
18
18
|
*/
|
|
19
19
|
export const xiaoyiDeleteCollectionTool = {
|
|
20
|
-
name: "
|
|
20
|
+
name: "delete_collection",
|
|
21
21
|
label: "Delete XiaoYi Collection",
|
|
22
22
|
description: `从小艺收藏中删除之前已保存的公共知识数据。任何用户希望删除已保存到个人知识库的数据都可以调用本技能。如果用户想更新之前的收藏数据,需要先query获取itemId然后再delete,最后执行Add,按照这个步骤完成收藏数据更新。
|
|
23
23
|
注意:
|