@ynhcj/xiaoyi-channel 0.0.51-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/src/bot.js +0 -16
- package/dist/src/client.js +0 -2
- package/dist/src/formatter.js +3 -23
- package/dist/src/heartbeat.js +0 -1
- package/dist/src/reply-dispatcher.js +32 -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 -8
- package/package.json +1 -1
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
// Image Reading tool implementation
|
|
2
2
|
import { XYFileUploadService } from "../file-upload.js";
|
|
3
3
|
import { getCurrentSessionContext } from "./session-manager.js";
|
|
4
|
-
import { logger } from "../utils/logger.js";
|
|
5
4
|
import fetch from "node-fetch";
|
|
6
5
|
import fs from "fs/promises";
|
|
7
6
|
import path from "path";
|
|
@@ -34,7 +33,6 @@ async function isLocalFile(value) {
|
|
|
34
33
|
* Download remote file to local temp directory
|
|
35
34
|
*/
|
|
36
35
|
async function downloadRemoteFile(url) {
|
|
37
|
-
logger.log(`[IMAGE_READING_TOOL] 📥 Downloading remote file: ${url}`);
|
|
38
36
|
try {
|
|
39
37
|
const response = await fetch(url);
|
|
40
38
|
if (!response.ok) {
|
|
@@ -56,11 +54,9 @@ async function downloadRemoteFile(url) {
|
|
|
56
54
|
const arrayBuffer = await response.arrayBuffer();
|
|
57
55
|
const buffer = Buffer.from(arrayBuffer);
|
|
58
56
|
await fs.writeFile(localPath, buffer);
|
|
59
|
-
logger.log(`[IMAGE_READING_TOOL] ✅ File downloaded to: ${localPath}`);
|
|
60
57
|
return localPath;
|
|
61
58
|
}
|
|
62
59
|
catch (error) {
|
|
63
|
-
logger.error(`[IMAGE_READING_TOOL] ❌ Failed to download file from ${url}:`, error);
|
|
64
60
|
throw new Error(`Failed to download remote file: ${error instanceof Error ? error.message : String(error)}`);
|
|
65
61
|
}
|
|
66
62
|
}
|
|
@@ -68,30 +64,22 @@ async function downloadRemoteFile(url) {
|
|
|
68
64
|
* Process image input: validate and convert local file to OBS URL, keep remote URL unchanged
|
|
69
65
|
*/
|
|
70
66
|
async function processImageInput(imageInput, uploadService) {
|
|
71
|
-
logger.log(`[IMAGE_READING_TOOL] 🔄 Processing image input: ${imageInput}`);
|
|
72
67
|
// Check if it's a remote URL
|
|
73
68
|
if (isRemoteUrl(imageInput)) {
|
|
74
|
-
logger.log(`[IMAGE_READING_TOOL] 🌐 Input is remote URL, downloading...`);
|
|
75
69
|
const localPath = await downloadRemoteFile(imageInput);
|
|
76
|
-
logger.log(`[IMAGE_READING_TOOL] 📤 Uploading downloaded file to OBS...`);
|
|
77
70
|
const imageUrl = await uploadService.uploadFileAndGetUrl(localPath, "TEMPORARY_MATERIAL_DOC");
|
|
78
71
|
if (!imageUrl) {
|
|
79
|
-
logger.error(`[IMAGE_READING_TOOL] ❌ Failed to get URL after upload`);
|
|
80
72
|
throw new Error("图片上传失败:无法获取图片访问地址");
|
|
81
73
|
}
|
|
82
|
-
logger.log(`[IMAGE_READING_TOOL] ✅ Uploaded to OBS: ${imageUrl}`);
|
|
83
74
|
return { imageUrl, localPath };
|
|
84
75
|
}
|
|
85
76
|
// Check if it's a local file
|
|
86
77
|
const isLocal = await isLocalFile(imageInput);
|
|
87
78
|
if (isLocal) {
|
|
88
|
-
logger.log(`[IMAGE_READING_TOOL] 📁 Input is local file, uploading...`);
|
|
89
79
|
const imageUrl = await uploadService.uploadFileAndGetUrl(imageInput, "TEMPORARY_MATERIAL_DOC");
|
|
90
80
|
if (!imageUrl) {
|
|
91
|
-
logger.error(`[IMAGE_READING_TOOL] ❌ Failed to get URL after upload`);
|
|
92
81
|
throw new Error("图片上传失败:无法获取图片访问地址");
|
|
93
82
|
}
|
|
94
|
-
logger.log(`[IMAGE_READING_TOOL] ✅ Uploaded to OBS: ${imageUrl}`);
|
|
95
83
|
return { imageUrl };
|
|
96
84
|
}
|
|
97
85
|
throw new Error(`Invalid image input: must be a remote URL or local file path, got: ${imageInput}`);
|
|
@@ -100,9 +88,6 @@ async function processImageInput(imageInput, uploadService) {
|
|
|
100
88
|
* Call image understanding API with streaming response
|
|
101
89
|
*/
|
|
102
90
|
async function callImageUnderstandingAPI(imageUrl, text, apiKey, uid) {
|
|
103
|
-
logger.log(`[IMAGE_READING_TOOL] 🧠 Calling image understanding API...`);
|
|
104
|
-
logger.log(`[IMAGE_READING_TOOL] - imageUrl: ${imageUrl}`);
|
|
105
|
-
logger.log(`[IMAGE_READING_TOOL] - prompt: ${text}`);
|
|
106
91
|
const apiUrl = "https://hag-drcn.op.dbankcloud.com/celia-claw/v1/sse-api/skill/execute";
|
|
107
92
|
const traceId = uuidv4();
|
|
108
93
|
const headers = {
|
|
@@ -149,7 +134,6 @@ async function callImageUnderstandingAPI(imageUrl, text, apiKey, uid) {
|
|
|
149
134
|
},
|
|
150
135
|
],
|
|
151
136
|
};
|
|
152
|
-
logger.log(`[IMAGE_READING_TOOL] 📡 Sending request with trace ID: ${traceId}`);
|
|
153
137
|
try {
|
|
154
138
|
const response = await fetch(apiUrl, {
|
|
155
139
|
method: "POST",
|
|
@@ -158,19 +142,14 @@ async function callImageUnderstandingAPI(imageUrl, text, apiKey, uid) {
|
|
|
158
142
|
// @ts-ignore - node-fetch supports this
|
|
159
143
|
timeout: 120000, // 2 minutes timeout
|
|
160
144
|
});
|
|
161
|
-
logger.log(`[IMAGE_READING_TOOL] 📨 Response status: ${response.status}`);
|
|
162
|
-
logger.log(`[IMAGE_READING_TOOL] 📨 Content-Type: ${response.headers.get("Content-Type")}`);
|
|
163
145
|
if (!response.ok) {
|
|
164
146
|
const errorText = await response.text();
|
|
165
|
-
logger.error(`[IMAGE_READING_TOOL] ❌ API request failed: ${response.status}`);
|
|
166
|
-
logger.error(`[IMAGE_READING_TOOL] ❌ Response: ${errorText}`);
|
|
167
147
|
throw new Error(`API request failed: ${response.status} ${response.statusText}`);
|
|
168
148
|
}
|
|
169
149
|
// Process SSE stream
|
|
170
150
|
let lastCaption = "";
|
|
171
151
|
let lineCount = 0;
|
|
172
152
|
let buffer = "";
|
|
173
|
-
logger.log(`[IMAGE_READING_TOOL] 📖 Reading SSE stream...`);
|
|
174
153
|
// Read the response body as a stream
|
|
175
154
|
if (!response.body) {
|
|
176
155
|
throw new Error("Response body is null");
|
|
@@ -198,29 +177,23 @@ async function callImageUnderstandingAPI(imageUrl, text, apiKey, uid) {
|
|
|
198
177
|
const streamContent = info.actionExecutorResult.reply.streamInfo.streamContent;
|
|
199
178
|
if (streamContent) {
|
|
200
179
|
lastCaption = streamContent;
|
|
201
|
-
logger.log(`[IMAGE_READING_TOOL] 📝 Updated caption (length: ${streamContent.length})`);
|
|
202
180
|
}
|
|
203
181
|
}
|
|
204
182
|
}
|
|
205
183
|
}
|
|
206
184
|
}
|
|
207
185
|
catch (parseError) {
|
|
208
|
-
logger.warn(`[IMAGE_READING_TOOL] ⚠️ Failed to parse JSON data:`, parseError);
|
|
209
186
|
}
|
|
210
187
|
}
|
|
211
188
|
}
|
|
212
189
|
}
|
|
213
190
|
}
|
|
214
|
-
logger.log(`[IMAGE_READING_TOOL] ✅ Stream processing complete`);
|
|
215
|
-
logger.log(`[IMAGE_READING_TOOL] - Total lines processed: ${lineCount}`);
|
|
216
|
-
logger.log(`[IMAGE_READING_TOOL] - Final caption length: ${lastCaption.length}`);
|
|
217
191
|
if (!lastCaption) {
|
|
218
192
|
throw new Error("No caption received from image understanding API");
|
|
219
193
|
}
|
|
220
194
|
return lastCaption;
|
|
221
195
|
}
|
|
222
196
|
catch (error) {
|
|
223
|
-
logger.error(`[IMAGE_READING_TOOL] ❌ API call failed:`, error);
|
|
224
197
|
throw error;
|
|
225
198
|
}
|
|
226
199
|
}
|
|
@@ -278,26 +251,17 @@ d. 返回图像理解的文本描述内容`,
|
|
|
278
251
|
},
|
|
279
252
|
},
|
|
280
253
|
async execute(toolCallId, params) {
|
|
281
|
-
logger.log(`[IMAGE_READING_TOOL] 🚀 Starting execution`);
|
|
282
|
-
logger.log(`[IMAGE_READING_TOOL] - toolCallId: ${toolCallId}`);
|
|
283
|
-
logger.log(`[IMAGE_READING_TOOL] - params:`, JSON.stringify(params));
|
|
284
|
-
logger.log(`[IMAGE_READING_TOOL] - timestamp: ${new Date().toISOString()}`);
|
|
285
254
|
// Validate that at least one parameter is provided
|
|
286
255
|
if (!params.localUrl && !params.remoteUrl) {
|
|
287
|
-
logger.error(`[IMAGE_READING_TOOL] ❌ Missing both localUrl and remoteUrl parameters`);
|
|
288
256
|
throw new Error("At least one of localUrl or remoteUrl must be provided");
|
|
289
257
|
}
|
|
290
258
|
// Get prompt (default to "描述这张图片内容")
|
|
291
259
|
const prompt = params.prompt || "描述这张图片内容";
|
|
292
|
-
logger.log(`[IMAGE_READING_TOOL] 📝 Using prompt: ${prompt}`);
|
|
293
260
|
// Get session context
|
|
294
|
-
logger.log(`[IMAGE_READING_TOOL] 🔍 Getting session context...`);
|
|
295
261
|
const sessionContext = getCurrentSessionContext();
|
|
296
262
|
if (!sessionContext) {
|
|
297
|
-
logger.error(`[IMAGE_READING_TOOL] ❌ No active session found!`);
|
|
298
263
|
throw new Error("No active XY session found. Image reading tool can only be used during an active conversation.");
|
|
299
264
|
}
|
|
300
|
-
logger.log(`[IMAGE_READING_TOOL] ✅ Session context found`);
|
|
301
265
|
const { config } = sessionContext;
|
|
302
266
|
// Create upload service
|
|
303
267
|
const uploadService = new XYFileUploadService(config.fileUploadUrl, config.apiKey, config.uid);
|
|
@@ -306,27 +270,19 @@ d. 返回图像理解的文本描述内容`,
|
|
|
306
270
|
try {
|
|
307
271
|
// Process image input (prefer localUrl over remoteUrl)
|
|
308
272
|
const imageInput = params.localUrl || params.remoteUrl;
|
|
309
|
-
logger.log(`[IMAGE_READING_TOOL] 🖼️ Processing image: ${imageInput}`);
|
|
310
273
|
processedImage = await processImageInput(imageInput, uploadService);
|
|
311
274
|
// Track downloaded file for cleanup
|
|
312
275
|
if (processedImage.localPath) {
|
|
313
276
|
downloadedFile = processedImage.localPath;
|
|
314
277
|
}
|
|
315
|
-
logger.log(`[IMAGE_READING_TOOL] ✅ Image processed successfully`);
|
|
316
|
-
logger.log(`[IMAGE_READING_TOOL] - OBS URL: ${processedImage.imageUrl}`);
|
|
317
278
|
// Call image understanding API
|
|
318
279
|
const caption = await callImageUnderstandingAPI(processedImage.imageUrl, prompt, config.apiKey, config.uid);
|
|
319
|
-
logger.log(`[IMAGE_READING_TOOL] 🎉 Image understanding completed successfully`);
|
|
320
|
-
logger.log(`[IMAGE_READING_TOOL] - Caption length: ${caption.length} characters`);
|
|
321
280
|
// Clean up downloaded file if any
|
|
322
281
|
if (downloadedFile) {
|
|
323
|
-
logger.log(`[IMAGE_READING_TOOL] 🧹 Cleaning up downloaded file...`);
|
|
324
282
|
try {
|
|
325
283
|
await fs.unlink(downloadedFile);
|
|
326
|
-
logger.log(`[IMAGE_READING_TOOL] ✅ Cleaned up: ${downloadedFile}`);
|
|
327
284
|
}
|
|
328
285
|
catch (error) {
|
|
329
|
-
logger.warn(`[IMAGE_READING_TOOL] ⚠️ Failed to clean up file:`, error);
|
|
330
286
|
}
|
|
331
287
|
}
|
|
332
288
|
return {
|
|
@@ -346,15 +302,12 @@ d. 返回图像理解的文本描述内容`,
|
|
|
346
302
|
catch (error) {
|
|
347
303
|
// Clean up downloaded file on error
|
|
348
304
|
if (downloadedFile) {
|
|
349
|
-
logger.log(`[IMAGE_READING_TOOL] 🧹 Cleaning up downloaded file after error...`);
|
|
350
305
|
try {
|
|
351
306
|
await fs.unlink(downloadedFile);
|
|
352
307
|
}
|
|
353
308
|
catch (cleanupError) {
|
|
354
|
-
logger.warn(`[IMAGE_READING_TOOL] ⚠️ Failed to clean up file:`, cleanupError);
|
|
355
309
|
}
|
|
356
310
|
}
|
|
357
|
-
logger.error(`[IMAGE_READING_TOOL] ❌ Execution failed:`, error);
|
|
358
311
|
const errorMessage = error instanceof Error ? error.message : "图片分析失败";
|
|
359
312
|
// Return error result instead of throwing
|
|
360
313
|
return {
|
|
@@ -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 location tool - gets user's current location.
|
|
7
6
|
* Returns WGS84 coordinates (latitude, longitude).
|
|
@@ -9,38 +8,22 @@ import { logger } from "../utils/logger.js";
|
|
|
9
8
|
export const locationTool = {
|
|
10
9
|
name: "get_user_location",
|
|
11
10
|
label: "Get User Location",
|
|
12
|
-
description: "获取用户当前位置(经纬度坐标,WGS84坐标系)。需要用户设备授权位置访问权限。注意:操作超时时间为60
|
|
11
|
+
description: "获取用户当前位置(经纬度坐标,WGS84坐标系)。需要用户设备授权位置访问权限。注意:操作超时时间为60秒,请勿重复调用此工具,如果超时或失败,最多重试一次。回复约束:如果工具返回没有授权或者其他报错,只需要完整描述没有授权或者其他报错内容即可,不需要主动给用户提供解决方案,例如告诉用户如何授权,如何解决报错等都是不需要的,请严格遵守。",
|
|
13
12
|
parameters: {
|
|
14
13
|
type: "object",
|
|
15
14
|
properties: {},
|
|
16
15
|
required: [],
|
|
17
16
|
},
|
|
18
17
|
async execute(toolCallId, params) {
|
|
19
|
-
logger.log(`[LOCATION_TOOL] 🚀 Starting execution`);
|
|
20
|
-
logger.log(`[LOCATION_TOOL] - toolCallId: ${toolCallId}`);
|
|
21
|
-
logger.log(`[LOCATION_TOOL] - params:`, JSON.stringify(params));
|
|
22
|
-
logger.log(`[LOCATION_TOOL] - timestamp: ${new Date().toISOString()}`);
|
|
23
18
|
// Get session context
|
|
24
|
-
logger.log(`[LOCATION_TOOL] 🔍 Attempting to get session context...`);
|
|
25
19
|
const sessionContext = getCurrentSessionContext();
|
|
26
20
|
if (!sessionContext) {
|
|
27
|
-
logger.error(`[LOCATION_TOOL] ❌ FAILED: No active session found!`);
|
|
28
|
-
logger.error(`[LOCATION_TOOL] - toolCallId: ${toolCallId}`);
|
|
29
|
-
logger.error(`[LOCATION_TOOL] - This suggests the session was not registered or already cleaned up`);
|
|
30
21
|
throw new Error("No active XY session found. Location tool can only be used during an active conversation.");
|
|
31
22
|
}
|
|
32
|
-
logger.log(`[LOCATION_TOOL] ✅ Session context found`);
|
|
33
|
-
logger.log(`[LOCATION_TOOL] - sessionId: ${sessionContext.sessionId}`);
|
|
34
|
-
logger.log(`[LOCATION_TOOL] - taskId: ${sessionContext.taskId}`);
|
|
35
|
-
logger.log(`[LOCATION_TOOL] - messageId: ${sessionContext.messageId}`);
|
|
36
|
-
logger.log(`[LOCATION_TOOL] - agentId: ${sessionContext.agentId}`);
|
|
37
23
|
const { config, sessionId, taskId, messageId } = sessionContext;
|
|
38
24
|
// Get WebSocket manager
|
|
39
|
-
logger.log(`[LOCATION_TOOL] 🔌 Getting WebSocket manager...`);
|
|
40
25
|
const wsManager = getXYWebSocketManager(config);
|
|
41
|
-
logger.log(`[LOCATION_TOOL] ✅ WebSocket manager obtained`);
|
|
42
26
|
// Build GetCurrentLocation command
|
|
43
|
-
logger.log(`[LOCATION_TOOL] 📦 Building GetCurrentLocation command...`);
|
|
44
27
|
const command = {
|
|
45
28
|
header: {
|
|
46
29
|
namespace: "Common",
|
|
@@ -72,25 +55,17 @@ export const locationTool = {
|
|
|
72
55
|
},
|
|
73
56
|
};
|
|
74
57
|
// Send command and wait for response (60 second timeout)
|
|
75
|
-
logger.log(`[LOCATION_TOOL] ⏳ Setting up promise to wait for location response...`);
|
|
76
|
-
logger.log(`[LOCATION_TOOL] - Timeout: 60 seconds`);
|
|
77
58
|
return new Promise((resolve, reject) => {
|
|
78
59
|
const timeout = setTimeout(() => {
|
|
79
|
-
logger.error(`[LOCATION_TOOL] ⏰ Timeout: No response received within 60 seconds`);
|
|
80
60
|
wsManager.off("data-event", handler);
|
|
81
61
|
reject(new Error("获取位置超时(60秒)"));
|
|
82
62
|
}, 60000);
|
|
83
63
|
// Listen for data events from WebSocket
|
|
84
64
|
const handler = (event) => {
|
|
85
|
-
logger.log(`[LOCATION_TOOL] 📨 Received data event:`, JSON.stringify(event));
|
|
86
65
|
if (event.intentName === "GetCurrentLocation") {
|
|
87
|
-
logger.log(`[LOCATION_TOOL] 🎯 GetCurrentLocation event received`);
|
|
88
|
-
logger.log(`[LOCATION_TOOL] - status: ${event.status}`);
|
|
89
66
|
clearTimeout(timeout);
|
|
90
67
|
wsManager.off("data-event", handler);
|
|
91
68
|
if (event.status === "success" && event.outputs) {
|
|
92
|
-
logger.log(`[LOCATION_TOOL] ✅ Location retrieved successfully`);
|
|
93
|
-
logger.log(`[LOCATION_TOOL] - outputs:`, JSON.stringify(event.outputs));
|
|
94
69
|
// 成功,直接返回完整的 event.outputs JSON 字符串
|
|
95
70
|
resolve({
|
|
96
71
|
content: [
|
|
@@ -102,18 +77,14 @@ export const locationTool = {
|
|
|
102
77
|
});
|
|
103
78
|
}
|
|
104
79
|
else {
|
|
105
|
-
logger.error(`[LOCATION_TOOL] ❌ Location retrieval failed`);
|
|
106
|
-
logger.error(`[LOCATION_TOOL] - status: ${event.status}`);
|
|
107
80
|
reject(new Error(`获取位置失败: ${event.status}`));
|
|
108
81
|
}
|
|
109
82
|
}
|
|
110
83
|
};
|
|
111
84
|
// Register event handler
|
|
112
85
|
// Note: The WebSocket manager needs to emit 'data-event' when receiving data events
|
|
113
|
-
logger.log(`[LOCATION_TOOL] 📡 Registering data-event handler on WebSocket manager`);
|
|
114
86
|
wsManager.on("data-event", handler);
|
|
115
87
|
// Send the command
|
|
116
|
-
logger.log(`[LOCATION_TOOL] 📤 Sending GetCurrentLocation command...`);
|
|
117
88
|
sendCommand({
|
|
118
89
|
config,
|
|
119
90
|
sessionId,
|
|
@@ -121,9 +92,7 @@ export const locationTool = {
|
|
|
121
92
|
messageId,
|
|
122
93
|
command,
|
|
123
94
|
}).then(() => {
|
|
124
|
-
logger.log(`[LOCATION_TOOL] ✅ Command sent successfully, waiting for response...`);
|
|
125
95
|
}).catch((error) => {
|
|
126
|
-
logger.error(`[LOCATION_TOOL] ❌ Failed to send command:`, error);
|
|
127
96
|
clearTimeout(timeout);
|
|
128
97
|
wsManager.off("data-event", handler);
|
|
129
98
|
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
|
// Enum definitions for alarm parameters (same as create-alarm-tool)
|
|
6
5
|
const ALARM_SNOOZE_DURATION_VALUES = [5, 10, 15, 20, 25, 30];
|
|
7
6
|
const ALARM_SNOOZE_TOTAL_VALUES = [0, 1, 3, 5, 10];
|
|
@@ -39,7 +38,9 @@ export const modifyAlarmTool = {
|
|
|
39
38
|
2. 调用此工具修改闹钟,传入 entityId 和需要修改的参数
|
|
40
39
|
3. 其余不涉及需改的参数,如果search_alarm 或 create_alarm的结果中有相应的值,需要一并填上,需要与原有的保持一致,防止不填采用默认值
|
|
41
40
|
|
|
42
|
-
注意事项:操作超时时间为60
|
|
41
|
+
注意事项:操作超时时间为60秒,请勿重复调用此工具,如果超时或失败,最多重试一次。
|
|
42
|
+
|
|
43
|
+
回复约束:如果工具返回没有授权或者其他报错,只需要完整描述没有授权或者其他报错内容即可,不需要主动给用户提供解决方案,例如告诉用户如何授权,如何解决报错等都是不需要的,请严格遵守。`,
|
|
43
44
|
parameters: {
|
|
44
45
|
type: "object",
|
|
45
46
|
properties: {
|
|
@@ -84,16 +85,10 @@ export const modifyAlarmTool = {
|
|
|
84
85
|
required: ["entityId"],
|
|
85
86
|
},
|
|
86
87
|
async execute(toolCallId, params) {
|
|
87
|
-
logger.log(`[MODIFY_ALARM_TOOL] 🚀 Starting execution`);
|
|
88
|
-
logger.log(`[MODIFY_ALARM_TOOL] - toolCallId: ${toolCallId}`);
|
|
89
|
-
logger.log(`[MODIFY_ALARM_TOOL] - params:`, JSON.stringify(params));
|
|
90
|
-
logger.log(`[MODIFY_ALARM_TOOL] - timestamp: ${new Date().toISOString()}`);
|
|
91
88
|
// ===== Validate required parameter: entityId =====
|
|
92
89
|
if (!params.entityId || typeof params.entityId !== "string") {
|
|
93
|
-
logger.error(`[MODIFY_ALARM_TOOL] ❌ Missing or invalid entityId`);
|
|
94
90
|
throw new Error("Missing required parameter: entityId must be a string obtained from search_alarm or create_alarm");
|
|
95
91
|
}
|
|
96
|
-
logger.log(`[MODIFY_ALARM_TOOL] - entityId: ${params.entityId}`);
|
|
97
92
|
// ===== Build intentParam with provided parameters =====
|
|
98
93
|
const intentParam = {
|
|
99
94
|
entityName: "Alarm",
|
|
@@ -102,98 +97,76 @@ export const modifyAlarmTool = {
|
|
|
102
97
|
// Parse and convert alarmTime if provided
|
|
103
98
|
if (params.alarmTime !== undefined && params.alarmTime !== null) {
|
|
104
99
|
if (typeof params.alarmTime !== "string") {
|
|
105
|
-
logger.error(`[MODIFY_ALARM_TOOL] ❌ Invalid alarmTime type`);
|
|
106
100
|
throw new Error("alarmTime must be a string in format YYYYMMDD hhmmss");
|
|
107
101
|
}
|
|
108
|
-
logger.log(`[MODIFY_ALARM_TOOL] 🕒 Parsing alarmTime: ${params.alarmTime}`);
|
|
109
102
|
const alarmTimeMs = parseAlarmTimeToTimestamp(params.alarmTime);
|
|
110
103
|
if (alarmTimeMs === null) {
|
|
111
|
-
logger.error(`[MODIFY_ALARM_TOOL] ❌ Invalid alarmTime format`);
|
|
112
104
|
throw new Error("Invalid alarmTime format. Required format: YYYYMMDD hhmmss (e.g., 20240315 143000)");
|
|
113
105
|
}
|
|
114
106
|
intentParam.alarmTime = alarmTimeMs;
|
|
115
|
-
logger.log(`[MODIFY_ALARM_TOOL] ✅ alarmTime converted to timestamp: ${alarmTimeMs}`);
|
|
116
107
|
}
|
|
117
108
|
// Add alarmTitle if provided
|
|
118
109
|
if (params.alarmTitle !== undefined && params.alarmTitle !== null) {
|
|
119
110
|
if (typeof params.alarmTitle !== "string") {
|
|
120
|
-
logger.error(`[MODIFY_ALARM_TOOL] ❌ Invalid alarmTitle type`);
|
|
121
111
|
throw new Error("alarmTitle must be a string");
|
|
122
112
|
}
|
|
123
113
|
intentParam.alarmTitle = params.alarmTitle;
|
|
124
|
-
logger.log(`[MODIFY_ALARM_TOOL] - alarmTitle: ${params.alarmTitle}`);
|
|
125
114
|
}
|
|
126
115
|
// Add alarmState if provided
|
|
127
116
|
if (params.alarmState !== undefined && params.alarmState !== null) {
|
|
128
117
|
if (typeof params.alarmState !== "number") {
|
|
129
|
-
logger.error(`[MODIFY_ALARM_TOOL] ❌ Invalid alarmState type`);
|
|
130
118
|
throw new Error("alarmState must be a number");
|
|
131
119
|
}
|
|
132
120
|
if (!ALARM_STATE_VALUES.includes(params.alarmState)) {
|
|
133
|
-
logger.error(`[MODIFY_ALARM_TOOL] ❌ Invalid alarmState value: ${params.alarmState}`);
|
|
134
121
|
throw new Error(`alarmState must be one of: ${ALARM_STATE_VALUES.join(", ")}`);
|
|
135
122
|
}
|
|
136
123
|
intentParam.alarmState = params.alarmState;
|
|
137
|
-
logger.log(`[MODIFY_ALARM_TOOL] - alarmState: ${params.alarmState}`);
|
|
138
124
|
}
|
|
139
125
|
// Add alarmSnoozeDuration if provided
|
|
140
126
|
if (params.alarmSnoozeDuration !== undefined && params.alarmSnoozeDuration !== null) {
|
|
141
127
|
if (typeof params.alarmSnoozeDuration !== "number") {
|
|
142
|
-
logger.error(`[MODIFY_ALARM_TOOL] ❌ Invalid alarmSnoozeDuration type`);
|
|
143
128
|
throw new Error("alarmSnoozeDuration must be a number");
|
|
144
129
|
}
|
|
145
130
|
if (!ALARM_SNOOZE_DURATION_VALUES.includes(params.alarmSnoozeDuration)) {
|
|
146
|
-
logger.error(`[MODIFY_ALARM_TOOL] ❌ Invalid alarmSnoozeDuration value: ${params.alarmSnoozeDuration}`);
|
|
147
131
|
throw new Error(`alarmSnoozeDuration must be one of: ${ALARM_SNOOZE_DURATION_VALUES.join(", ")}`);
|
|
148
132
|
}
|
|
149
133
|
intentParam.alarmSnoozeDuration = params.alarmSnoozeDuration;
|
|
150
|
-
logger.log(`[MODIFY_ALARM_TOOL] - alarmSnoozeDuration: ${params.alarmSnoozeDuration}`);
|
|
151
134
|
}
|
|
152
135
|
// Add alarmSnoozeTotal if provided
|
|
153
136
|
if (params.alarmSnoozeTotal !== undefined && params.alarmSnoozeTotal !== null) {
|
|
154
137
|
if (typeof params.alarmSnoozeTotal !== "number") {
|
|
155
|
-
logger.error(`[MODIFY_ALARM_TOOL] ❌ Invalid alarmSnoozeTotal type`);
|
|
156
138
|
throw new Error("alarmSnoozeTotal must be a number");
|
|
157
139
|
}
|
|
158
140
|
if (!ALARM_SNOOZE_TOTAL_VALUES.includes(params.alarmSnoozeTotal)) {
|
|
159
|
-
logger.error(`[MODIFY_ALARM_TOOL] ❌ Invalid alarmSnoozeTotal value: ${params.alarmSnoozeTotal}`);
|
|
160
141
|
throw new Error(`alarmSnoozeTotal must be one of: ${ALARM_SNOOZE_TOTAL_VALUES.join(", ")}`);
|
|
161
142
|
}
|
|
162
143
|
intentParam.alarmSnoozeTotal = params.alarmSnoozeTotal;
|
|
163
|
-
logger.log(`[MODIFY_ALARM_TOOL] - alarmSnoozeTotal: ${params.alarmSnoozeTotal}`);
|
|
164
144
|
}
|
|
165
145
|
// Add alarmRingDuration if provided
|
|
166
146
|
if (params.alarmRingDuration !== undefined && params.alarmRingDuration !== null) {
|
|
167
147
|
if (typeof params.alarmRingDuration !== "number") {
|
|
168
|
-
logger.error(`[MODIFY_ALARM_TOOL] ❌ Invalid alarmRingDuration type`);
|
|
169
148
|
throw new Error("alarmRingDuration must be a number");
|
|
170
149
|
}
|
|
171
150
|
if (!ALARM_RING_DURATION_VALUES.includes(params.alarmRingDuration)) {
|
|
172
|
-
logger.error(`[MODIFY_ALARM_TOOL] ❌ Invalid alarmRingDuration value: ${params.alarmRingDuration}`);
|
|
173
151
|
throw new Error(`alarmRingDuration must be one of: ${ALARM_RING_DURATION_VALUES.join(", ")}`);
|
|
174
152
|
}
|
|
175
153
|
intentParam.alarmRingDuration = params.alarmRingDuration;
|
|
176
|
-
logger.log(`[MODIFY_ALARM_TOOL] - alarmRingDuration: ${params.alarmRingDuration}`);
|
|
177
154
|
}
|
|
178
155
|
// Add daysOfWakeType if provided
|
|
179
156
|
if (params.daysOfWakeType !== undefined && params.daysOfWakeType !== null) {
|
|
180
157
|
if (typeof params.daysOfWakeType !== "number") {
|
|
181
|
-
logger.error(`[MODIFY_ALARM_TOOL] ❌ Invalid daysOfWakeType type`);
|
|
182
158
|
throw new Error("daysOfWakeType must be a number");
|
|
183
159
|
}
|
|
184
160
|
if (!DAYS_OF_WAKE_TYPE_VALUES.includes(params.daysOfWakeType)) {
|
|
185
|
-
logger.error(`[MODIFY_ALARM_TOOL] ❌ Invalid daysOfWakeType value: ${params.daysOfWakeType}`);
|
|
186
161
|
throw new Error(`daysOfWakeType must be one of: ${DAYS_OF_WAKE_TYPE_VALUES.join(", ")}`);
|
|
187
162
|
}
|
|
188
163
|
intentParam.daysOfWakeType = params.daysOfWakeType;
|
|
189
|
-
logger.log(`[MODIFY_ALARM_TOOL] - daysOfWakeType: ${params.daysOfWakeType}`);
|
|
190
164
|
}
|
|
191
165
|
// Add daysOfWeek if provided - only valid when daysOfWakeType is 3
|
|
192
166
|
if (params.daysOfWeek !== undefined && params.daysOfWeek !== null) {
|
|
193
167
|
// Check if daysOfWakeType is 3 or will be set to 3
|
|
194
168
|
const targetDaysOfWakeType = params.daysOfWakeType !== undefined ? params.daysOfWakeType : null;
|
|
195
169
|
if (targetDaysOfWakeType !== null && targetDaysOfWakeType !== 3) {
|
|
196
|
-
logger.warn(`[MODIFY_ALARM_TOOL] ⚠️ daysOfWeek parameter is ignored when daysOfWakeType is not 3 (current: ${targetDaysOfWakeType}). Please remove daysOfWeek parameter.`);
|
|
197
170
|
// Skip processing daysOfWeek when daysOfWakeType is not 3
|
|
198
171
|
}
|
|
199
172
|
else {
|
|
@@ -201,73 +174,53 @@ export const modifyAlarmTool = {
|
|
|
201
174
|
let normalizedDaysOfWeek = null;
|
|
202
175
|
// 情况1: 已经是数组
|
|
203
176
|
if (Array.isArray(params.daysOfWeek)) {
|
|
204
|
-
logger.log(`[MODIFY_ALARM_TOOL] ✅ daysOfWeek is already an array`);
|
|
205
177
|
normalizedDaysOfWeek = params.daysOfWeek;
|
|
206
178
|
}
|
|
207
179
|
// 情况2: 是字符串,尝试解析为 JSON 数组
|
|
208
180
|
else if (typeof params.daysOfWeek === 'string') {
|
|
209
|
-
logger.log(`[MODIFY_ALARM_TOOL] 🔄 daysOfWeek is a string, attempting to parse as JSON...`);
|
|
210
181
|
try {
|
|
211
182
|
const parsed = JSON.parse(params.daysOfWeek);
|
|
212
183
|
if (Array.isArray(parsed)) {
|
|
213
|
-
logger.log(`[MODIFY_ALARM_TOOL] ✅ Successfully parsed JSON string to array`);
|
|
214
184
|
normalizedDaysOfWeek = parsed;
|
|
215
185
|
}
|
|
216
186
|
else {
|
|
217
|
-
logger.error(`[MODIFY_ALARM_TOOL] ❌ Parsed JSON is not an array:`, typeof parsed);
|
|
218
187
|
throw new Error("daysOfWeek must be an array or a JSON string representing an array");
|
|
219
188
|
}
|
|
220
189
|
}
|
|
221
190
|
catch (parseError) {
|
|
222
|
-
logger.error(`[MODIFY_ALARM_TOOL] ❌ Failed to parse daysOfWeek as JSON:`, parseError);
|
|
223
191
|
throw new Error(`daysOfWeek must be a valid JSON array string. Parse error: ${parseError instanceof Error ? parseError.message : String(parseError)}`);
|
|
224
192
|
}
|
|
225
193
|
}
|
|
226
194
|
// 情况3: 其他类型,报错
|
|
227
195
|
else {
|
|
228
|
-
logger.error(`[MODIFY_ALARM_TOOL] ❌ Invalid daysOfWeek type:`, typeof params.daysOfWeek);
|
|
229
196
|
throw new Error(`daysOfWeek must be an array or a JSON string, got ${typeof params.daysOfWeek}`);
|
|
230
197
|
}
|
|
231
198
|
// 验证数组非空
|
|
232
199
|
if (!normalizedDaysOfWeek || normalizedDaysOfWeek.length === 0) {
|
|
233
|
-
logger.error(`[MODIFY_ALARM_TOOL] ❌ daysOfWeek array is empty`);
|
|
234
200
|
throw new Error("daysOfWeek array cannot be empty");
|
|
235
201
|
}
|
|
236
202
|
// 验证数组长度必须为1
|
|
237
203
|
if (normalizedDaysOfWeek.length !== 1) {
|
|
238
|
-
logger.error(`[MODIFY_ALARM_TOOL] ❌ daysOfWeek array length must be 1, got ${normalizedDaysOfWeek.length}`);
|
|
239
204
|
throw new Error("daysOfWeek 仅支持长度为1的数组。如果需要一周中不同的几天,需要多次调用此工具");
|
|
240
205
|
}
|
|
241
206
|
// Validate each day
|
|
242
207
|
for (const day of normalizedDaysOfWeek) {
|
|
243
208
|
if (typeof day !== "string" || !DAYS_OF_WEEK_VALUES.includes(day)) {
|
|
244
|
-
logger.error(`[MODIFY_ALARM_TOOL] ❌ Invalid day value: ${day}`);
|
|
245
209
|
throw new Error(`daysOfWeek must contain only: ${DAYS_OF_WEEK_VALUES.join(", ")}`);
|
|
246
210
|
}
|
|
247
211
|
}
|
|
248
212
|
intentParam.daysOfWeek = normalizedDaysOfWeek;
|
|
249
|
-
logger.log(`[MODIFY_ALARM_TOOL] - daysOfWeek: ${normalizedDaysOfWeek.join(", ")}`);
|
|
250
213
|
}
|
|
251
214
|
}
|
|
252
215
|
// Get session context
|
|
253
|
-
logger.log(`[MODIFY_ALARM_TOOL] 🔍 Attempting to get session context...`);
|
|
254
216
|
const sessionContext = getCurrentSessionContext();
|
|
255
217
|
if (!sessionContext) {
|
|
256
|
-
logger.error(`[MODIFY_ALARM_TOOL] ❌ FAILED: No active session found!`);
|
|
257
|
-
logger.error(`[MODIFY_ALARM_TOOL] - toolCallId: ${toolCallId}`);
|
|
258
218
|
throw new Error("No active XY session found. Modify alarm tool can only be used during an active conversation.");
|
|
259
219
|
}
|
|
260
|
-
logger.log(`[MODIFY_ALARM_TOOL] ✅ Session context found`);
|
|
261
|
-
logger.log(`[MODIFY_ALARM_TOOL] - sessionId: ${sessionContext.sessionId}`);
|
|
262
|
-
logger.log(`[MODIFY_ALARM_TOOL] - taskId: ${sessionContext.taskId}`);
|
|
263
|
-
logger.log(`[MODIFY_ALARM_TOOL] - messageId: ${sessionContext.messageId}`);
|
|
264
220
|
const { config, sessionId, taskId, messageId } = sessionContext;
|
|
265
221
|
// Get WebSocket manager
|
|
266
|
-
logger.log(`[MODIFY_ALARM_TOOL] 🔌 Getting WebSocket manager...`);
|
|
267
222
|
const wsManager = getXYWebSocketManager(config);
|
|
268
|
-
logger.log(`[MODIFY_ALARM_TOOL] ✅ WebSocket manager obtained`);
|
|
269
223
|
// Build ModifyAlarm command
|
|
270
|
-
logger.log(`[MODIFY_ALARM_TOOL] 📦 Building ModifyAlarm command...`);
|
|
271
224
|
const command = {
|
|
272
225
|
header: {
|
|
273
226
|
namespace: "Common",
|
|
@@ -300,25 +253,17 @@ export const modifyAlarmTool = {
|
|
|
300
253
|
},
|
|
301
254
|
};
|
|
302
255
|
// Send command and wait for response (60 second timeout)
|
|
303
|
-
logger.log(`[MODIFY_ALARM_TOOL] ⏳ Setting up promise to wait for alarm modification response...`);
|
|
304
|
-
logger.log(`[MODIFY_ALARM_TOOL] - Timeout: 60 seconds`);
|
|
305
256
|
return new Promise((resolve, reject) => {
|
|
306
257
|
const timeout = setTimeout(() => {
|
|
307
|
-
logger.error(`[MODIFY_ALARM_TOOL] ⏰ Timeout: No response received within 60 seconds`);
|
|
308
258
|
wsManager.off("data-event", handler);
|
|
309
259
|
reject(new Error("修改闹钟超时(60秒)"));
|
|
310
260
|
}, 60000);
|
|
311
261
|
// Listen for data events from WebSocket
|
|
312
262
|
const handler = (event) => {
|
|
313
|
-
logger.log(`[MODIFY_ALARM_TOOL] 📨 Received data event:`, JSON.stringify(event));
|
|
314
263
|
if (event.intentName === "ModifyAlarm") {
|
|
315
|
-
logger.log(`[MODIFY_ALARM_TOOL] 🎯 ModifyAlarm event received`);
|
|
316
|
-
logger.log(`[MODIFY_ALARM_TOOL] - status: ${event.status}`);
|
|
317
264
|
clearTimeout(timeout);
|
|
318
265
|
wsManager.off("data-event", handler);
|
|
319
266
|
if (event.status === "success" && event.outputs) {
|
|
320
|
-
logger.log(`[MODIFY_ALARM_TOOL] ✅ Alarm modification completed successfully`);
|
|
321
|
-
logger.log(`[MODIFY_ALARM_TOOL] - outputs:`, JSON.stringify(event.outputs));
|
|
322
267
|
// 成功,直接返回完整的 event.outputs JSON 字符串
|
|
323
268
|
resolve({
|
|
324
269
|
content: [
|
|
@@ -330,18 +275,13 @@ export const modifyAlarmTool = {
|
|
|
330
275
|
});
|
|
331
276
|
}
|
|
332
277
|
else {
|
|
333
|
-
logger.error(`[MODIFY_ALARM_TOOL] ❌ Alarm modification failed`);
|
|
334
|
-
logger.error(`[MODIFY_ALARM_TOOL] - status: ${event.status}`);
|
|
335
|
-
logger.error(`[MODIFY_ALARM_TOOL] - outputs:`, JSON.stringify(event.outputs || {}));
|
|
336
278
|
reject(new Error(`修改闹钟失败: ${event.status}`));
|
|
337
279
|
}
|
|
338
280
|
}
|
|
339
281
|
};
|
|
340
282
|
// Register event handler
|
|
341
|
-
logger.log(`[MODIFY_ALARM_TOOL] 📡 Registering data-event handler on WebSocket manager`);
|
|
342
283
|
wsManager.on("data-event", handler);
|
|
343
284
|
// Send the command
|
|
344
|
-
logger.log(`[MODIFY_ALARM_TOOL] 📤 Sending ModifyAlarm command...`);
|
|
345
285
|
sendCommand({
|
|
346
286
|
config,
|
|
347
287
|
sessionId,
|
|
@@ -350,10 +290,8 @@ export const modifyAlarmTool = {
|
|
|
350
290
|
command,
|
|
351
291
|
})
|
|
352
292
|
.then(() => {
|
|
353
|
-
logger.log(`[MODIFY_ALARM_TOOL] ✅ Command sent successfully, waiting for response...`);
|
|
354
293
|
})
|
|
355
294
|
.catch((error) => {
|
|
356
|
-
logger.error(`[MODIFY_ALARM_TOOL] ❌ Failed to send command:`, error);
|
|
357
295
|
clearTimeout(timeout);
|
|
358
296
|
wsManager.off("data-event", handler);
|
|
359
297
|
reject(error);
|
|
@@ -375,17 +313,14 @@ function parseAlarmTimeToTimestamp(alarmTime) {
|
|
|
375
313
|
const trimmed = alarmTime.trim();
|
|
376
314
|
// Check basic format (should have at least 13 characters: YYYYMMDD hhmmss)
|
|
377
315
|
if (trimmed.length < 13) {
|
|
378
|
-
logger.error(`[MODIFY_ALARM_TOOL] ❌ alarmTime too short: ${trimmed}`);
|
|
379
316
|
return null;
|
|
380
317
|
}
|
|
381
318
|
// Extract date and time parts
|
|
382
319
|
// Format: YYYYMMDD hhmmss
|
|
383
320
|
const datePart = trimmed.substring(0, 8); // YYYYMMDD
|
|
384
321
|
const timePart = trimmed.substring(8).trim(); // hhmmss (may have leading space)
|
|
385
|
-
logger.log(`[MODIFY_ALARM_TOOL] - datePart: ${datePart}, timePart: ${timePart}`);
|
|
386
322
|
// Validate lengths
|
|
387
323
|
if (datePart.length !== 8 || timePart.length !== 6) {
|
|
388
|
-
logger.error(`[MODIFY_ALARM_TOOL] ❌ Invalid part lengths: datePart=${datePart.length}, timePart=${timePart.length}`);
|
|
389
324
|
return null;
|
|
390
325
|
}
|
|
391
326
|
// Parse components
|
|
@@ -395,45 +330,36 @@ function parseAlarmTimeToTimestamp(alarmTime) {
|
|
|
395
330
|
const hour = parseInt(timePart.substring(0, 2), 10);
|
|
396
331
|
const minute = parseInt(timePart.substring(2, 4), 10);
|
|
397
332
|
const second = parseInt(timePart.substring(4, 6), 10);
|
|
398
|
-
logger.log(`[MODIFY_ALARM_TOOL] - Parsed: ${year}-${month}-${day} ${hour}:${minute}:${second}`);
|
|
399
333
|
// Validate values
|
|
400
334
|
if (isNaN(year) || isNaN(month) || isNaN(day) ||
|
|
401
335
|
isNaN(hour) || isNaN(minute) || isNaN(second)) {
|
|
402
|
-
logger.error(`[MODIFY_ALARM_TOOL] ❌ NaN detected in parsed values`);
|
|
403
336
|
return null;
|
|
404
337
|
}
|
|
405
338
|
// Validate ranges
|
|
406
339
|
if (month < 1 || month > 12) {
|
|
407
|
-
logger.error(`[MODIFY_ALARM_TOOL] ❌ Invalid month: ${month}`);
|
|
408
340
|
return null;
|
|
409
341
|
}
|
|
410
342
|
if (day < 1 || day > 31) {
|
|
411
|
-
logger.error(`[MODIFY_ALARM_TOOL] ❌ Invalid day: ${day}`);
|
|
412
343
|
return null;
|
|
413
344
|
}
|
|
414
345
|
if (hour < 0 || hour > 23) {
|
|
415
|
-
logger.error(`[MODIFY_ALARM_TOOL] ❌ Invalid hour: ${hour}`);
|
|
416
346
|
return null;
|
|
417
347
|
}
|
|
418
348
|
if (minute < 0 || minute > 59) {
|
|
419
|
-
logger.error(`[MODIFY_ALARM_TOOL] ❌ Invalid minute: ${minute}`);
|
|
420
349
|
return null;
|
|
421
350
|
}
|
|
422
351
|
if (second < 0 || second > 59) {
|
|
423
|
-
logger.error(`[MODIFY_ALARM_TOOL] ❌ Invalid second: ${second}`);
|
|
424
352
|
return null;
|
|
425
353
|
}
|
|
426
354
|
// Create Date object and get timestamp
|
|
427
355
|
const date = new Date(year, month - 1, day, hour, minute, second);
|
|
428
356
|
const timestamp = date.getTime();
|
|
429
357
|
if (isNaN(timestamp)) {
|
|
430
|
-
logger.error(`[MODIFY_ALARM_TOOL] ❌ Generated timestamp is NaN`);
|
|
431
358
|
return null;
|
|
432
359
|
}
|
|
433
360
|
return timestamp;
|
|
434
361
|
}
|
|
435
362
|
catch (error) {
|
|
436
|
-
logger.error(`[MODIFY_ALARM_TOOL] ❌ Exception in parseAlarmTimeToTimestamp:`, error);
|
|
437
363
|
return null;
|
|
438
364
|
}
|
|
439
365
|
}
|