@ynhcj/xiaoyi-channel 0.0.26-beta → 0.0.28-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.
Files changed (36) hide show
  1. package/dist/src/bot.js +13 -3
  2. package/dist/src/channel.js +11 -1
  3. package/dist/src/outbound.js +88 -76
  4. package/dist/src/tools/calendar-tool.js +2 -2
  5. package/dist/src/tools/call-phone-tool.d.ts +5 -0
  6. package/dist/src/tools/call-phone-tool.js +183 -0
  7. package/dist/src/tools/create-alarm-tool.d.ts +7 -0
  8. package/dist/src/tools/create-alarm-tool.js +444 -0
  9. package/dist/src/tools/delete-alarm-tool.d.ts +11 -0
  10. package/dist/src/tools/delete-alarm-tool.js +238 -0
  11. package/dist/src/tools/location-tool.js +2 -2
  12. package/dist/src/tools/modify-alarm-tool.d.ts +9 -0
  13. package/dist/src/tools/modify-alarm-tool.js +474 -0
  14. package/dist/src/tools/modify-note-tool.js +2 -2
  15. package/dist/src/tools/note-tool.js +2 -2
  16. package/dist/src/tools/search-alarm-tool.d.ts +8 -0
  17. package/dist/src/tools/search-alarm-tool.js +389 -0
  18. package/dist/src/tools/search-calendar-tool.js +2 -2
  19. package/dist/src/tools/search-contact-tool.js +2 -2
  20. package/dist/src/tools/search-file-tool.d.ts +5 -0
  21. package/dist/src/tools/search-file-tool.js +173 -0
  22. package/dist/src/tools/search-message-tool.d.ts +5 -0
  23. package/dist/src/tools/search-message-tool.js +173 -0
  24. package/dist/src/tools/search-note-tool.js +2 -2
  25. package/dist/src/tools/search-photo-gallery-tool.js +2 -2
  26. package/dist/src/tools/send-file-to-user-tool.d.ts +5 -0
  27. package/dist/src/tools/send-file-to-user-tool.js +290 -0
  28. package/dist/src/tools/send-message-tool.d.ts +5 -0
  29. package/dist/src/tools/send-message-tool.js +189 -0
  30. package/dist/src/tools/session-manager.d.ts +12 -0
  31. package/dist/src/tools/session-manager.js +33 -0
  32. package/dist/src/tools/upload-file-tool.d.ts +13 -0
  33. package/dist/src/tools/upload-file-tool.js +265 -0
  34. package/dist/src/tools/upload-photo-tool.js +2 -2
  35. package/dist/src/tools/xiaoyi-gui-tool.js +2 -2
  36. package/package.json +1 -1
package/dist/src/bot.js CHANGED
@@ -4,7 +4,7 @@ import { parseA2AMessage, extractTextFromParts, extractFileParts, extractPushId
4
4
  import { downloadFilesFromParts } from "./file-download.js";
5
5
  import { resolveXYConfig } from "./config.js";
6
6
  import { sendStatusUpdate, sendClearContextResponse, sendTasksCancelResponse } from "./formatter.js";
7
- import { registerSession, unregisterSession } from "./tools/session-manager.js";
7
+ import { registerSession, unregisterSession, runWithSessionContext } from "./tools/session-manager.js";
8
8
  import { configManager } from "./utils/config-manager.js";
9
9
  import { registerTaskId, decrementTaskIdRef, lockTaskId, unlockTaskId, hasActiveTask, } from "./task-manager.js";
10
10
  /**
@@ -200,6 +200,14 @@ export async function handleXYMessage(params) {
200
200
  log(`xy: dispatching to agent (session=${parsed.sessionId})`);
201
201
  // Dispatch to OpenClaw core using correct API (following feishu pattern)
202
202
  log(`[BOT] 🚀 Starting dispatcher with session: ${route.sessionKey}`);
203
+ // Build session context for AsyncLocalStorage
204
+ const sessionContext = {
205
+ config,
206
+ sessionId: parsed.sessionId,
207
+ taskId: parsed.taskId,
208
+ messageId: parsed.messageId,
209
+ agentId: route.accountId,
210
+ };
203
211
  await core.channel.reply.withReplyDispatcher({
204
212
  dispatcher,
205
213
  onSettled: () => {
@@ -217,12 +225,14 @@ export async function handleXYMessage(params) {
217
225
  unregisterSession(route.sessionKey);
218
226
  log(`[BOT] ✅ Cleanup completed`);
219
227
  },
220
- run: () => core.channel.reply.dispatchReplyFromConfig({
228
+ run: () =>
229
+ // 🔐 Use AsyncLocalStorage to provide session context to tools
230
+ runWithSessionContext(sessionContext, () => core.channel.reply.dispatchReplyFromConfig({
221
231
  ctx: ctxPayload,
222
232
  cfg,
223
233
  dispatcher,
224
234
  replyOptions,
225
- }),
235
+ })),
226
236
  });
227
237
  log(`[BOT] ✅ Dispatcher completed for session: ${parsed.sessionId}`);
228
238
  log(`xy: dispatch complete (session=${parsed.sessionId})`);
@@ -12,6 +12,16 @@ import { searchCalendarTool } from "./tools/search-calendar-tool.js";
12
12
  import { searchPhotoGalleryTool } from "./tools/search-photo-gallery-tool.js";
13
13
  import { uploadPhotoTool } from "./tools/upload-photo-tool.js";
14
14
  import { xiaoyiGuiTool } from "./tools/xiaoyi-gui-tool.js";
15
+ import { callPhoneTool } from "./tools/call-phone-tool.js";
16
+ import { searchMessageTool } from "./tools/search-message-tool.js";
17
+ import { searchFileTool } from "./tools/search-file-tool.js";
18
+ import { uploadFileTool } from "./tools/upload-file-tool.js";
19
+ import { createAlarmTool } from "./tools/create-alarm-tool.js";
20
+ import { searchAlarmTool } from "./tools/search-alarm-tool.js";
21
+ import { modifyAlarmTool } from "./tools/modify-alarm-tool.js";
22
+ import { deleteAlarmTool } from "./tools/delete-alarm-tool.js";
23
+ import { sendMessageTool } from "./tools/send-message-tool.js";
24
+ import { sendFileToUserTool } from "./tools/send-file-to-user-tool.js";
15
25
  /**
16
26
  * Xiaoyi Channel Plugin for OpenClaw.
17
27
  * Implements Xiaoyi A2A protocol with dual WebSocket connections.
@@ -51,7 +61,7 @@ export const xyPlugin = {
51
61
  },
52
62
  outbound: xyOutbound,
53
63
  onboarding: xyOnboardingAdapter,
54
- agentTools: [locationTool, noteTool, searchNoteTool, modifyNoteTool, calendarTool, searchCalendarTool, searchPhotoGalleryTool, uploadPhotoTool, xiaoyiGuiTool], // searchContactTool 已暂时禁用
64
+ agentTools: [locationTool, noteTool, searchNoteTool, modifyNoteTool, calendarTool, searchCalendarTool, searchPhotoGalleryTool, uploadPhotoTool, xiaoyiGuiTool, callPhoneTool, searchMessageTool, searchFileTool, uploadFileTool, createAlarmTool, searchAlarmTool, modifyAlarmTool, deleteAlarmTool, sendMessageTool, sendFileToUserTool], // searchContactTool 已暂时禁用
55
65
  messaging: {
56
66
  normalizeTarget: (raw) => {
57
67
  const trimmed = raw.trim();
@@ -1,7 +1,6 @@
1
1
  import { resolveXYConfig } from "./config.js";
2
- import { XYFileUploadService } from "./file-upload.js";
3
2
  import { XYPushService } from "./push.js";
4
- import { getLatestSessionContext } from "./tools/session-manager.js";
3
+ import { getCurrentSessionContext } from "./tools/session-manager.js";
5
4
  // Special marker for default push delivery when no target is specified
6
5
  const DEFAULT_PUSH_MARKER = "default";
7
6
  // File extension to MIME type mapping
@@ -65,8 +64,8 @@ export const xyOutbound = {
65
64
  // If the target doesn't contain "::", try to enhance it with taskId from session context
66
65
  if (!trimmedTo.includes("::")) {
67
66
  console.log(`[xyOutbound.resolveTarget] Target "${trimmedTo}" missing taskId, looking up session context`);
68
- // Try to get the latest session context
69
- const sessionContext = getLatestSessionContext();
67
+ // Try to get the current session context
68
+ const sessionContext = getCurrentSessionContext();
70
69
  if (sessionContext && sessionContext.sessionId === trimmedTo) {
71
70
  const enhancedTarget = `${trimmedTo}::${sessionContext.taskId}`;
72
71
  console.log(`[xyOutbound.resolveTarget] Enhanced target: ${enhancedTarget}`);
@@ -130,81 +129,94 @@ export const xyOutbound = {
130
129
  mediaUrl,
131
130
  mediaLocalRoots,
132
131
  });
133
- // Parse to: "sessionId::taskId"
134
- const parts = to.split("::");
135
- if (parts.length !== 2) {
136
- throw new Error(`Invalid to format: "${to}". Expected "sessionId::taskId"`);
137
- }
138
- const [sessionId, taskId] = parts;
139
- // Resolve configuration
140
- const config = resolveXYConfig(cfg);
141
- // Create upload service
142
- const uploadService = new XYFileUploadService(config.fileUploadUrl, config.apiKey, config.uid);
143
- // Validate mediaUrl
144
- if (!mediaUrl) {
145
- throw new Error("mediaUrl is required for sendMedia");
146
- }
147
- // Upload file
148
- const fileId = await uploadService.uploadFile(mediaUrl);
149
- // Check if fileId is empty
150
- if (!fileId) {
151
- console.log(`[xyOutbound.sendMedia] ⚠️ File upload failed: fileId is empty, aborting sendMedia`);
152
- return {
153
- channel: "xiaoyi-channel",
154
- messageId: "",
155
- chatId: to,
156
- };
157
- }
158
- console.log(`[xyOutbound.sendMedia] File uploaded:`, {
159
- fileId,
160
- sessionId,
161
- taskId,
162
- });
163
- // Get filename and mime type from mediaUrl
164
- // mediaUrl may be a local file path or URL
165
- const fileName = mediaUrl.split("/").pop() || "unknown";
166
- const mimeType = getMimeTypeFromFilename(fileName);
167
- // Build agent_response message
168
- const agentResponse = {
169
- msgType: "agent_response",
170
- agentId: config.agentId,
171
- sessionId: sessionId,
172
- taskId: taskId,
173
- msgDetail: JSON.stringify({
174
- jsonrpc: "2.0",
175
- id: taskId,
176
- result: {
177
- kind: "artifact-update",
178
- append: true,
179
- lastChunk: false,
180
- final: false,
181
- artifact: {
182
- artifactId: taskId,
183
- parts: [
184
- {
185
- kind: "file",
186
- file: {
187
- name: fileName,
188
- mimeType: mimeType,
189
- fileId: fileId,
190
- },
191
- },
192
- ],
193
- },
194
- },
195
- error: { code: 0 },
196
- }),
197
- };
198
- // Get WebSocket manager and send message
199
- const { getXYWebSocketManager } = await import("./client.js");
200
- const wsManager = getXYWebSocketManager(config);
201
- await wsManager.sendMessage(sessionId, agentResponse);
202
- console.log(`[xyOutbound.sendMedia] WebSocket message sent successfully`);
203
- // Return message info
132
+ // All sendMedia processing logic has been disabled
133
+ // Use send_file_to_user tool instead for file transfers to user device
134
+ console.log(`[xyOutbound.sendMedia] Processing disabled, use send_file_to_user tool`);
135
+ // Return empty message info
204
136
  return {
205
137
  channel: "xiaoyi-channel",
206
- messageId: fileId,
138
+ messageId: "",
207
139
  chatId: to,
208
140
  };
141
+ // // Parse to: "sessionId::taskId"
142
+ // const parts = to.split("::");
143
+ // if (parts.length !== 2) {
144
+ // throw new Error(`Invalid to format: "${to}". Expected "sessionId::taskId"`);
145
+ // }
146
+ // const [sessionId, taskId] = parts;
147
+ // // Resolve configuration
148
+ // const config = resolveXYConfig(cfg);
149
+ // // Create upload service
150
+ // const uploadService = new XYFileUploadService(
151
+ // config.fileUploadUrl,
152
+ // config.apiKey,
153
+ // config.uid
154
+ // );
155
+ // // Validate mediaUrl
156
+ // if (!mediaUrl) {
157
+ // throw new Error("mediaUrl is required for sendMedia");
158
+ // }
159
+ // // Upload file
160
+ // const fileId = await uploadService.uploadFile(mediaUrl);
161
+ // // Check if fileId is empty
162
+ // if (!fileId) {
163
+ // console.log(`[xyOutbound.sendMedia] ⚠️ File upload failed: fileId is empty, aborting sendMedia`);
164
+ // return {
165
+ // channel: "xiaoyi-channel",
166
+ // messageId: "",
167
+ // chatId: to,
168
+ // };
169
+ // }
170
+ // console.log(`[xyOutbound.sendMedia] File uploaded:`, {
171
+ // fileId,
172
+ // sessionId,
173
+ // taskId,
174
+ // });
175
+ // // Get filename and mime type from mediaUrl
176
+ // // mediaUrl may be a local file path or URL
177
+ // const fileName = mediaUrl.split("/").pop() || "unknown";
178
+ // const mimeType = getMimeTypeFromFilename(fileName);
179
+ // // Build agent_response message
180
+ // const agentResponse: OutboundWebSocketMessage = {
181
+ // msgType: "agent_response",
182
+ // agentId: config.agentId,
183
+ // sessionId: sessionId,
184
+ // taskId: taskId,
185
+ // msgDetail: JSON.stringify({
186
+ // jsonrpc: "2.0",
187
+ // id: taskId,
188
+ // result: {
189
+ // kind: "artifact-update",
190
+ // append: true,
191
+ // lastChunk: false,
192
+ // final: false,
193
+ // artifact: {
194
+ // artifactId: taskId,
195
+ // parts: [
196
+ // {
197
+ // kind: "file",
198
+ // file: {
199
+ // name: fileName,
200
+ // mimeType: mimeType,
201
+ // fileId: fileId,
202
+ // },
203
+ // },
204
+ // ],
205
+ // },
206
+ // },
207
+ // error: { code: 0 },
208
+ // }),
209
+ // };
210
+ // // Get WebSocket manager and send message
211
+ // const { getXYWebSocketManager } = await import("./client.js");
212
+ // const wsManager = getXYWebSocketManager(config);
213
+ // await wsManager.sendMessage(sessionId, agentResponse);
214
+ // console.log(`[xyOutbound.sendMedia] WebSocket message sent successfully`);
215
+ // // Return message info
216
+ // return {
217
+ // channel: "xiaoyi-channel",
218
+ // messageId: fileId,
219
+ // chatId: to,
220
+ // };
209
221
  },
210
222
  };
@@ -1,6 +1,6 @@
1
1
  import { getXYWebSocketManager } from "../client.js";
2
2
  import { sendCommand } from "../formatter.js";
3
- import { getLatestSessionContext } from "./session-manager.js";
3
+ import { getCurrentSessionContext } from "./session-manager.js";
4
4
  import { logger } from "../utils/logger.js";
5
5
  /**
6
6
  * XY calendar event tool - creates a calendar event on user's device.
@@ -54,7 +54,7 @@ export const calendarTool = {
54
54
  logger.log(`[CALENDAR_TOOL] - dtEnd timestamp: ${dtEndMs}`);
55
55
  // Get session context
56
56
  logger.log(`[CALENDAR_TOOL] 🔍 Attempting to get session context...`);
57
- const sessionContext = getLatestSessionContext();
57
+ const sessionContext = getCurrentSessionContext();
58
58
  if (!sessionContext) {
59
59
  logger.error(`[CALENDAR_TOOL] ❌ FAILED: No active session found!`);
60
60
  logger.error(`[CALENDAR_TOOL] - toolCallId: ${toolCallId}`);
@@ -0,0 +1,5 @@
1
+ /**
2
+ * XY call phone tool - makes a phone call on user's device.
3
+ * Requires phoneNumber parameter and optional slotId (0 for primary SIM, 1 for secondary SIM).
4
+ */
5
+ export declare const callPhoneTool: any;
@@ -0,0 +1,183 @@
1
+ import { getXYWebSocketManager } from "../client.js";
2
+ import { sendCommand } from "../formatter.js";
3
+ import { getCurrentSessionContext } from "./session-manager.js";
4
+ import { logger } from "../utils/logger.js";
5
+ /**
6
+ * XY call phone tool - makes a phone call on user's device.
7
+ * Requires phoneNumber parameter and optional slotId (0 for primary SIM, 1 for secondary SIM).
8
+ */
9
+ export const callPhoneTool = {
10
+ name: "call_phone",
11
+ label: "Call Phone",
12
+ description: "拨打电话。需要提供要拨打的电话号码。slotId参数可选,默认为0(主卡),如果用户明确要求使用副卡则设置为1。注意:操作超时时间为60秒,请勿重复调用此工具,如果超时或失败,最多重试一次。",
13
+ parameters: {
14
+ type: "object",
15
+ properties: {
16
+ phoneNumber: {
17
+ type: "string",
18
+ description: "要拨打的电话号码",
19
+ },
20
+ slotId: {
21
+ type: "number",
22
+ description: "SIM卡槽ID,默认为0(主卡),设置为1表示副卡。仅当用户明确要求使用副卡时才设置为1",
23
+ default: 0,
24
+ },
25
+ },
26
+ required: ["phoneNumber"],
27
+ },
28
+ async execute(toolCallId, params) {
29
+ logger.log(`[CALL_PHONE_TOOL] 🚀 Starting execution`);
30
+ logger.log(`[CALL_PHONE_TOOL] - toolCallId: ${toolCallId}`);
31
+ logger.log(`[CALL_PHONE_TOOL] - params:`, JSON.stringify(params));
32
+ logger.log(`[CALL_PHONE_TOOL] - timestamp: ${new Date().toISOString()}`);
33
+ // Validate phoneNumber parameter
34
+ if (!params.phoneNumber || typeof params.phoneNumber !== "string" || params.phoneNumber.trim() === "") {
35
+ logger.error(`[CALL_PHONE_TOOL] ❌ Missing or invalid phoneNumber parameter`);
36
+ throw new Error("Missing required parameter: phoneNumber must be a non-empty string");
37
+ }
38
+ // Set default slotId if not provided
39
+ const slotId = params.slotId !== undefined && params.slotId !== null ? params.slotId : 0;
40
+ // Validate slotId (must be 0 or 1)
41
+ if (slotId !== 0 && slotId !== 1) {
42
+ logger.error(`[CALL_PHONE_TOOL] ❌ Invalid slotId: ${slotId}`);
43
+ throw new Error("Invalid slotId: must be 0 (primary SIM) or 1 (secondary SIM)");
44
+ }
45
+ logger.log(`[CALL_PHONE_TOOL] 📞 Preparing to call phone number: ${params.phoneNumber}`);
46
+ logger.log(`[CALL_PHONE_TOOL] - slotId: ${slotId} (${slotId === 0 ? "主卡" : "副卡"})`);
47
+ // Get session context
48
+ logger.log(`[CALL_PHONE_TOOL] 🔍 Attempting to get session context...`);
49
+ const sessionContext = getCurrentSessionContext();
50
+ if (!sessionContext) {
51
+ logger.error(`[CALL_PHONE_TOOL] ❌ FAILED: No active session found!`);
52
+ logger.error(`[CALL_PHONE_TOOL] - toolCallId: ${toolCallId}`);
53
+ throw new Error("No active XY session found. Call phone tool can only be used during an active conversation.");
54
+ }
55
+ logger.log(`[CALL_PHONE_TOOL] ✅ Session context found`);
56
+ logger.log(`[CALL_PHONE_TOOL] - sessionId: ${sessionContext.sessionId}`);
57
+ logger.log(`[CALL_PHONE_TOOL] - taskId: ${sessionContext.taskId}`);
58
+ logger.log(`[CALL_PHONE_TOOL] - messageId: ${sessionContext.messageId}`);
59
+ const { config, sessionId, taskId, messageId } = sessionContext;
60
+ // Get WebSocket manager
61
+ logger.log(`[CALL_PHONE_TOOL] 🔌 Getting WebSocket manager...`);
62
+ const wsManager = getXYWebSocketManager(config);
63
+ logger.log(`[CALL_PHONE_TOOL] ✅ WebSocket manager obtained`);
64
+ // Build StartCall command
65
+ logger.log(`[CALL_PHONE_TOOL] 📦 Building StartCall command...`);
66
+ const command = {
67
+ header: {
68
+ namespace: "Common",
69
+ name: "Action",
70
+ },
71
+ payload: {
72
+ cardParam: {},
73
+ executeParam: {
74
+ executeMode: "background",
75
+ intentName: "StartCall",
76
+ bundleName: "com.huawei.hmos.aidispatchservice",
77
+ dimension: "",
78
+ needUnlock: true,
79
+ actionResponse: true,
80
+ timeOut: 5,
81
+ intentParam: {
82
+ phoneNumber: params.phoneNumber.trim(),
83
+ slotId: slotId,
84
+ },
85
+ permissionId: [],
86
+ achieveType: "INTENT",
87
+ },
88
+ responses: [
89
+ {
90
+ resultCode: "",
91
+ displayText: "",
92
+ ttsText: "",
93
+ },
94
+ ],
95
+ needUploadResult: true,
96
+ noHalfPage: false,
97
+ pageControlRelated: false,
98
+ },
99
+ };
100
+ logger.log(`[CALL_PHONE_TOOL] 📋 Command details:`, JSON.stringify(command, null, 2));
101
+ // Send command and wait for response (60 second timeout)
102
+ logger.log(`[CALL_PHONE_TOOL] ⏳ Setting up promise to wait for call response...`);
103
+ logger.log(`[CALL_PHONE_TOOL] - Timeout: 60 seconds`);
104
+ return new Promise((resolve, reject) => {
105
+ const timeout = setTimeout(() => {
106
+ logger.error(`[CALL_PHONE_TOOL] ⏰ Timeout: No response received within 60 seconds`);
107
+ wsManager.off("data-event", handler);
108
+ reject(new Error("拨打电话超时(60秒)"));
109
+ }, 60000);
110
+ // Listen for data events from WebSocket
111
+ const handler = (event) => {
112
+ logger.log(`[CALL_PHONE_TOOL] 📨 Received data event:`, JSON.stringify(event));
113
+ if (event.intentName === "StartCall") {
114
+ logger.log(`[CALL_PHONE_TOOL] 🎯 StartCall event received`);
115
+ logger.log(`[CALL_PHONE_TOOL] - status: ${event.status}`);
116
+ clearTimeout(timeout);
117
+ wsManager.off("data-event", handler);
118
+ if (event.status === "success" && event.outputs) {
119
+ logger.log(`[CALL_PHONE_TOOL] ✅ Call response received`);
120
+ logger.log(`[CALL_PHONE_TOOL] - outputs:`, JSON.stringify(event.outputs));
121
+ // Check for error code in outputs
122
+ const code = event.outputs.code !== undefined ? event.outputs.code : null;
123
+ if (code !== null && code !== 0) {
124
+ logger.error(`[CALL_PHONE_TOOL] ❌ Device returned error`);
125
+ logger.error(`[CALL_PHONE_TOOL] - code: ${code}`);
126
+ const errorMsg = event.outputs.errorMsg || event.outputs.errMsg || "未知错误";
127
+ logger.error(`[CALL_PHONE_TOOL] - errorMsg: ${errorMsg}`);
128
+ reject(new Error(`拨打电话失败: ${errorMsg} (错误代码: ${code})`));
129
+ return;
130
+ }
131
+ // Return the outputs directly
132
+ const result = {
133
+ success: true,
134
+ code: code,
135
+ phoneNumber: params.phoneNumber,
136
+ slotId: slotId,
137
+ message: "电话拨打成功",
138
+ };
139
+ logger.log(`[CALL_PHONE_TOOL] 🎉 Call initiated successfully`);
140
+ logger.log(`[CALL_PHONE_TOOL] - phoneNumber: ${params.phoneNumber}`);
141
+ logger.log(`[CALL_PHONE_TOOL] - slotId: ${slotId}`);
142
+ resolve({
143
+ content: [
144
+ {
145
+ type: "text",
146
+ text: JSON.stringify(result),
147
+ },
148
+ ],
149
+ });
150
+ }
151
+ else {
152
+ logger.error(`[CALL_PHONE_TOOL] ❌ Call failed`);
153
+ logger.error(`[CALL_PHONE_TOOL] - status: ${event.status}`);
154
+ logger.error(`[CALL_PHONE_TOOL] - outputs:`, JSON.stringify(event.outputs || {}));
155
+ const errorDetail = event.outputs ? JSON.stringify(event.outputs) : event.status;
156
+ reject(new Error(`拨打电话失败: ${errorDetail}`));
157
+ }
158
+ }
159
+ };
160
+ // Register event handler
161
+ logger.log(`[CALL_PHONE_TOOL] 📡 Registering data-event handler on WebSocket manager`);
162
+ wsManager.on("data-event", handler);
163
+ // Send the command
164
+ logger.log(`[CALL_PHONE_TOOL] 📤 Sending StartCall command...`);
165
+ sendCommand({
166
+ config,
167
+ sessionId,
168
+ taskId,
169
+ messageId,
170
+ command,
171
+ })
172
+ .then(() => {
173
+ logger.log(`[CALL_PHONE_TOOL] ✅ Command sent successfully, waiting for response...`);
174
+ })
175
+ .catch((error) => {
176
+ logger.error(`[CALL_PHONE_TOOL] ❌ Failed to send command:`, error);
177
+ clearTimeout(timeout);
178
+ wsManager.off("data-event", handler);
179
+ reject(error);
180
+ });
181
+ });
182
+ },
183
+ };
@@ -0,0 +1,7 @@
1
+ /**
2
+ * XY create alarm tool - creates an alarm on user's device.
3
+ * Requires alarmTime parameter. Other parameters are optional with sensible defaults.
4
+ *
5
+ * Time format: YYYYMMDD hhmmss (e.g., 20240315 143000)
6
+ */
7
+ export declare const createAlarmTool: any;