@ynhcj/xiaoyi-channel 0.0.89-beta → 0.0.90-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.
@@ -7,6 +7,7 @@ import { sendFileToUserTool } from "./tools/send-file-to-user-tool.js";
7
7
  import { viewPushResultTool } from "./tools/view-push-result-tool.js";
8
8
  import { imageReadingTool } from "./tools/image-reading-tool.js";
9
9
  import { timestampToUtc8Tool } from "./tools/timestamp-to-utc8-tool.js";
10
+ import { sendEmailTool } from "./tools/send-email-tool.js";
10
11
  import { searchEmailTool } from "./tools/search-email-tool.js";
11
12
  import { callDeviceTool } from "./tools/call-device-tool.js";
12
13
  import { getNoteToolSchemaTool } from "./tools/get-note-tool-schema.js";
@@ -58,7 +59,7 @@ export const xyPlugin = {
58
59
  },
59
60
  outbound: xyOutbound,
60
61
  agentTools: () => {
61
- const allTools = [locationTool, callDeviceTool, getNoteToolSchemaTool, getCalendarToolSchemaTool, getContactToolSchemaTool, getPhotoToolSchemaTool, xiaoyiGuiTool, getDeviceFileToolSchemaTool, getAlarmToolSchemaTool, getCollectionToolSchemaTool, sendFileToUserTool, viewPushResultTool, imageReadingTool, timestampToUtc8Tool, searchEmailTool];
62
+ const allTools = [locationTool, callDeviceTool, getNoteToolSchemaTool, getCalendarToolSchemaTool, getContactToolSchemaTool, getPhotoToolSchemaTool, xiaoyiGuiTool, getDeviceFileToolSchemaTool, getAlarmToolSchemaTool, getCollectionToolSchemaTool, sendFileToUserTool, viewPushResultTool, imageReadingTool, timestampToUtc8Tool, sendEmailTool, searchEmailTool];
62
63
  const ctx = getCurrentSessionContext();
63
64
  const filtered = filterToolsByDevice(allTools, ctx?.deviceType);
64
65
  logger.log(`[DEVICE-FILTER] deviceType=${ctx?.deviceType ?? "(none)"}, tools: ${allTools.length} → ${filtered.length} (${filtered.map(t => t.name).join(", ")})`);
@@ -0,0 +1,4 @@
1
+ /**
2
+ * XY send email tool - sends an email via 花瓣邮箱 on user's device.
3
+ */
4
+ export declare const sendEmailTool: any;
@@ -0,0 +1,134 @@
1
+ // Send Email tool implementation
2
+ import { getXYWebSocketManager } from "../client.js";
3
+ import { sendCommand } from "../formatter.js";
4
+ import { getCurrentSessionContext } from "./session-manager.js";
5
+ class ToolInputError extends Error {
6
+ status = 400;
7
+ constructor(message) {
8
+ super(message);
9
+ this.name = "ToolInputError";
10
+ }
11
+ }
12
+ /**
13
+ * XY send email tool - sends an email via 花瓣邮箱 on user's device.
14
+ */
15
+ export const sendEmailTool = {
16
+ name: "send_email",
17
+ label: "Send Email",
18
+ description: `在用户设备上通过花瓣邮箱发送邮件。
19
+ 注意:
20
+ a. 操作超时时间为60秒,请勿重复调用此工具
21
+ b. 如果遇到各类调用失败场景,最多只能重试一次,不可以重复调用多次。
22
+ c. 调用工具前需认真检查调用参数是否满足工具要求
23
+
24
+ 回复约束:如果工具返回没有授权或者其他报错,只需要完整描述没有授权或者其他报错内容即可,不需要主动给用户提供解决方案,例如告诉用户如何授权,如何解决报错等都是不需要的,请严格遵守。`,
25
+ parameters: {
26
+ type: "object",
27
+ properties: {
28
+ subject: {
29
+ type: "string",
30
+ description: "邮件主题,必填",
31
+ },
32
+ to: {
33
+ type: "string",
34
+ description: "收件人邮箱地址,必填",
35
+ },
36
+ body: {
37
+ type: "string",
38
+ description: "邮件内容,必填",
39
+ },
40
+ },
41
+ required: ["subject", "to", "body"],
42
+ },
43
+ async execute(_toolCallId, params) {
44
+ if (typeof params.subject !== "string" || !params.subject.trim()) {
45
+ throw new ToolInputError("缺少必填参数 subject(邮件主题)");
46
+ }
47
+ if (typeof params.to !== "string" || !params.to.trim()) {
48
+ throw new ToolInputError("缺少必填参数 to(收件人邮箱地址)");
49
+ }
50
+ if (typeof params.body !== "string" || !params.body.trim()) {
51
+ throw new ToolInputError("缺少必填参数 body(邮件内容)");
52
+ }
53
+ const sessionContext = getCurrentSessionContext();
54
+ if (!sessionContext) {
55
+ throw new Error("No active XY session found. Send email tool can only be used during an active conversation.");
56
+ }
57
+ const { config, sessionId, taskId, messageId } = sessionContext;
58
+ const wsManager = getXYWebSocketManager(config);
59
+ const command = {
60
+ header: {
61
+ namespace: "Common",
62
+ name: "Action",
63
+ },
64
+ payload: {
65
+ cardParam: {},
66
+ executeParam: {
67
+ executeMode: "background",
68
+ intentName: "SendEmail",
69
+ bundleName: "com.huawei.hmos.email",
70
+ needUnlock: true,
71
+ actionResponse: true,
72
+ appType: "OHOS_APP",
73
+ timeOut: 5,
74
+ intentParam: {
75
+ subject: params.subject.trim(),
76
+ to: [params.to.trim()],
77
+ body: params.body.trim(),
78
+ },
79
+ permissionId: [],
80
+ achieveType: "INTENT",
81
+ },
82
+ responses: [
83
+ {
84
+ resultCode: "",
85
+ displayText: "",
86
+ ttsText: "",
87
+ },
88
+ ],
89
+ needUploadResult: true,
90
+ noHalfPage: false,
91
+ pageControlRelated: false,
92
+ },
93
+ };
94
+ return new Promise((resolve, reject) => {
95
+ const timeout = setTimeout(() => {
96
+ wsManager.off("data-event", handler);
97
+ reject(new Error("发送邮件超时(60秒)"));
98
+ }, 60000);
99
+ const handler = (event) => {
100
+ if (event.intentName === "SendEmail") {
101
+ clearTimeout(timeout);
102
+ wsManager.off("data-event", handler);
103
+ if (event.status === "success" && event.outputs) {
104
+ resolve({
105
+ content: [
106
+ {
107
+ type: "text",
108
+ text: JSON.stringify(event.outputs),
109
+ },
110
+ ],
111
+ });
112
+ }
113
+ else {
114
+ reject(new Error(`发送邮件失败: ${event.status}`));
115
+ }
116
+ }
117
+ };
118
+ wsManager.on("data-event", handler);
119
+ sendCommand({
120
+ config,
121
+ sessionId,
122
+ taskId,
123
+ messageId,
124
+ command,
125
+ })
126
+ .then(() => { })
127
+ .catch((error) => {
128
+ clearTimeout(timeout);
129
+ wsManager.off("data-event", handler);
130
+ reject(error);
131
+ });
132
+ });
133
+ },
134
+ };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ynhcj/xiaoyi-channel",
3
- "version": "0.0.89-beta",
3
+ "version": "0.0.90-beta",
4
4
  "description": "OpenClaw Xiaoyi Channel plugin - Xiaoyi A2A protocol integration",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",