@ynhcj/xiaoyi-channel 0.0.9 โ†’ 0.0.10-next

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 (79) hide show
  1. package/dist/src/bot.js +153 -48
  2. package/dist/src/channel.js +23 -4
  3. package/dist/src/client.d.ts +15 -0
  4. package/dist/src/client.js +81 -0
  5. package/dist/src/config.js +2 -2
  6. package/dist/src/file-download.js +10 -1
  7. package/dist/src/formatter.d.ts +31 -0
  8. package/dist/src/formatter.js +93 -1
  9. package/dist/src/heartbeat.d.ts +2 -1
  10. package/dist/src/heartbeat.js +6 -1
  11. package/dist/src/monitor.d.ts +5 -0
  12. package/dist/src/monitor.js +101 -9
  13. package/dist/src/outbound.js +97 -7
  14. package/dist/src/parser.d.ts +12 -0
  15. package/dist/src/parser.js +37 -0
  16. package/dist/src/push.d.ts +13 -1
  17. package/dist/src/push.js +125 -19
  18. package/dist/src/reply-dispatcher.d.ts +1 -0
  19. package/dist/src/reply-dispatcher.js +206 -51
  20. package/dist/src/task-manager.d.ts +55 -0
  21. package/dist/src/task-manager.js +136 -0
  22. package/dist/src/tools/calendar-tool.d.ts +6 -0
  23. package/dist/src/tools/calendar-tool.js +169 -0
  24. package/dist/src/tools/call-phone-tool.d.ts +5 -0
  25. package/dist/src/tools/call-phone-tool.js +183 -0
  26. package/dist/src/tools/create-alarm-tool.d.ts +7 -0
  27. package/dist/src/tools/create-alarm-tool.js +446 -0
  28. package/dist/src/tools/delete-alarm-tool.d.ts +11 -0
  29. package/dist/src/tools/delete-alarm-tool.js +238 -0
  30. package/dist/src/tools/location-tool.js +18 -8
  31. package/dist/src/tools/modify-alarm-tool.d.ts +9 -0
  32. package/dist/src/tools/modify-alarm-tool.js +467 -0
  33. package/dist/src/tools/modify-note-tool.d.ts +9 -0
  34. package/dist/src/tools/modify-note-tool.js +163 -0
  35. package/dist/src/tools/note-tool.js +32 -11
  36. package/dist/src/tools/search-alarm-tool.d.ts +8 -0
  37. package/dist/src/tools/search-alarm-tool.js +391 -0
  38. package/dist/src/tools/search-calendar-tool.d.ts +12 -0
  39. package/dist/src/tools/search-calendar-tool.js +262 -0
  40. package/dist/src/tools/search-contact-tool.d.ts +5 -0
  41. package/dist/src/tools/search-contact-tool.js +168 -0
  42. package/dist/src/tools/search-file-tool.d.ts +5 -0
  43. package/dist/src/tools/search-file-tool.js +185 -0
  44. package/dist/src/tools/search-message-tool.d.ts +5 -0
  45. package/dist/src/tools/search-message-tool.js +173 -0
  46. package/dist/src/tools/search-note-tool.js +6 -6
  47. package/dist/src/tools/search-photo-gallery-tool.d.ts +8 -0
  48. package/dist/src/tools/search-photo-gallery-tool.js +212 -0
  49. package/dist/src/tools/search-photo-tool.d.ts +9 -0
  50. package/dist/src/tools/search-photo-tool.js +270 -0
  51. package/dist/src/tools/send-file-to-user-tool.d.ts +5 -0
  52. package/dist/src/tools/send-file-to-user-tool.js +318 -0
  53. package/dist/src/tools/send-message-tool.d.ts +5 -0
  54. package/dist/src/tools/send-message-tool.js +189 -0
  55. package/dist/src/tools/session-manager.d.ts +15 -0
  56. package/dist/src/tools/session-manager.js +101 -13
  57. package/dist/src/tools/upload-file-tool.d.ts +13 -0
  58. package/dist/src/tools/upload-file-tool.js +265 -0
  59. package/dist/src/tools/upload-photo-tool.d.ts +9 -0
  60. package/dist/src/tools/upload-photo-tool.js +223 -0
  61. package/dist/src/tools/view-push-result-tool.d.ts +5 -0
  62. package/dist/src/tools/view-push-result-tool.js +118 -0
  63. package/dist/src/tools/xiaoyi-collection-tool.d.ts +5 -0
  64. package/dist/src/tools/xiaoyi-collection-tool.js +190 -0
  65. package/dist/src/tools/xiaoyi-gui-tool.d.ts +6 -0
  66. package/dist/src/tools/xiaoyi-gui-tool.js +151 -0
  67. package/dist/src/trigger-handler.d.ts +22 -0
  68. package/dist/src/trigger-handler.js +59 -0
  69. package/dist/src/types.d.ts +6 -17
  70. package/dist/src/types.js +4 -0
  71. package/dist/src/utils/config-manager.d.ts +26 -0
  72. package/dist/src/utils/config-manager.js +56 -0
  73. package/dist/src/utils/pushdata-manager.d.ts +28 -0
  74. package/dist/src/utils/pushdata-manager.js +171 -0
  75. package/dist/src/utils/pushid-manager.d.ts +12 -0
  76. package/dist/src/utils/pushid-manager.js +105 -0
  77. package/dist/src/websocket.d.ts +59 -25
  78. package/dist/src/websocket.js +315 -257
  79. package/package.json +1 -1
@@ -1,7 +1,19 @@
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
+ /**
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
+ }
5
17
  /**
6
18
  * XY note tool - creates a note on user's device.
7
19
  * Requires title and content parameters.
@@ -9,29 +21,38 @@ import { logger } from "../utils/logger.js";
9
21
  export const noteTool = {
10
22
  name: "create_note",
11
23
  label: "Create Note",
12
- description: "ๅœจ็”จๆˆท่ฎพๅค‡ไธŠๅˆ›ๅปบๅค‡ๅฟ˜ๅฝ•ใ€‚้œ€่ฆๆไพ›ๅค‡ๅฟ˜ๅฝ•ๆ ‡้ข˜ๅ’Œๅ†…ๅฎนใ€‚",
24
+ description: `ๅœจ็”จๆˆท่ฎพๅค‡ไธŠๅˆ›ๅปบๅค‡ๅฟ˜ๅฝ•ใ€‚้œ€่ฆๆไพ›ๅค‡ๅฟ˜ๅฝ•ๆ ‡้ข˜ๅ’Œๅ†…ๅฎนใ€‚
25
+ ๆณจๆ„:
26
+ a. ๆ“ไฝœ่ถ…ๆ—ถๆ—ถ้—ดไธบ60็ง’,่ฏทๅ‹ฟ้‡ๅค่ฐƒ็”จๆญคๅทฅๅ…ท
27
+ b. ๅฆ‚ๆžœ้‡ๅˆฐๅ„็ฑป่ฐƒ็”จๅคฑ่ดฅๅœบๆ™ฏ,ๆœ€ๅคšๅช่ƒฝ้‡่ฏ•ไธ€ๆฌก๏ผŒไธๅฏไปฅ้‡ๅค่ฐƒ็”จๅคšๆฌกใ€‚
28
+ c. ่ฐƒ็”จๅทฅๅ…ทๅ‰้œ€่ฎค็œŸๆฃ€ๆŸฅ่ฐƒ็”จๅ‚ๆ•ฐๆ˜ฏๅฆๆปก่ถณๅทฅๅ…ท่ฆๆฑ‚
29
+ `,
13
30
  parameters: {
14
31
  type: "object",
15
32
  properties: {
16
33
  title: {
17
34
  type: "string",
18
- description: "ๅค‡ๅฟ˜ๅฝ•ๆ ‡้ข˜",
35
+ description: "ๅค‡ๅฟ˜ๅฝ•ๆ ‡้ข˜๏ผŒๅฟ…ๅกซ",
19
36
  },
20
37
  content: {
21
38
  type: "string",
22
- description: "ๅค‡ๅฟ˜ๅฝ•ๅ†…ๅฎน",
39
+ description: "ๅค‡ๅฟ˜ๅฝ•ๅ†…ๅฎน๏ผŒๅฟ…ๅกซ",
23
40
  },
24
41
  },
25
42
  required: ["title", "content"],
26
43
  },
27
44
  async execute(toolCallId, params) {
28
45
  logger.debug("Executing note tool, toolCallId:", toolCallId);
29
- // Validate parameters
30
- if (!params.title || !params.content) {
31
- throw new Error("Missing required parameters: title and content are required");
46
+ // Validate parameters โ€” ๆŠ› ToolInputError ่€Œ้žๆ™ฎ้€š Error๏ผŒ
47
+ // ่ฎฉ openclaw ่ฟ”ๅ›ž 400 ่€Œ้ž 500๏ผŒๆ˜Ž็กฎๅ‘Š็Ÿฅ LLM ่ฟ™ๆ˜ฏๅ‚ๆ•ฐ้”™่ฏฏ๏ผŒไธๅบ”้‡่ฏ•ใ€‚
48
+ if (typeof params.title !== "string" || !params.title) {
49
+ throw new ToolInputError("็ผบๅฐ‘ๅฟ…ๅกซๅ‚ๆ•ฐ title๏ผˆๅค‡ๅฟ˜ๅฝ•ๆ ‡้ข˜๏ผ‰");
50
+ }
51
+ if (typeof params.content !== "string" || !params.content) {
52
+ throw new ToolInputError("็ผบๅฐ‘ๅฟ…ๅกซๅ‚ๆ•ฐ content๏ผˆๅค‡ๅฟ˜ๅฝ•ๅ†…ๅฎน๏ผ‰");
32
53
  }
33
54
  // Get session context
34
- const sessionContext = getLatestSessionContext();
55
+ const sessionContext = getCurrentSessionContext();
35
56
  if (!sessionContext) {
36
57
  throw new Error("No active XY session found. Note tool can only be used during an active conversation.");
37
58
  }
@@ -72,12 +93,12 @@ export const noteTool = {
72
93
  pageControlRelated: false,
73
94
  },
74
95
  };
75
- // Send command and wait for response (5 second timeout)
96
+ // Send command and wait for response (60 second timeout)
76
97
  return new Promise((resolve, reject) => {
77
98
  const timeout = setTimeout(() => {
78
99
  wsManager.off("data-event", handler);
79
- reject(new Error("ๅˆ›ๅปบๅค‡ๅฟ˜ๅฝ•่ถ…ๆ—ถ๏ผˆ5็ง’๏ผ‰"));
80
- }, 5000);
100
+ reject(new Error("ๅˆ›ๅปบๅค‡ๅฟ˜ๅฝ•่ถ…ๆ—ถ๏ผˆ60็ง’๏ผ‰"));
101
+ }, 60000);
81
102
  // Listen for data events from WebSocket
82
103
  const handler = (event) => {
83
104
  logger.debug("Received data event:", event);
@@ -0,0 +1,8 @@
1
+ /**
2
+ * XY search alarm tool - searches alarms on user's device.
3
+ * Returns matching alarms based on various filter criteria.
4
+ *
5
+ * At least one search criterion must be provided.
6
+ * Multiple criteria can be combined.
7
+ */
8
+ export declare const searchAlarmTool: any;
@@ -0,0 +1,391 @@
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
+ // Enum definitions for alarm search parameters
6
+ const RANGE_TYPE_VALUES = ["all", "next", "current"];
7
+ const ALARM_STATE_VALUES = [0, 1];
8
+ const DAYS_OF_WAKE_TYPE_VALUES = [0, 1, 2, 3, 4];
9
+ /**
10
+ * XY search alarm tool - searches alarms on user's device.
11
+ * Returns matching alarms based on various filter criteria.
12
+ *
13
+ * At least one search criterion must be provided.
14
+ * Multiple criteria can be combined.
15
+ */
16
+ export const searchAlarmTool = {
17
+ name: "search_alarm",
18
+ label: "Search Alarm",
19
+ description: `ๆฃ€็ดข็”จๆˆท่ฎพๅค‡ไธŠ็š„้—น้’Ÿใ€‚ๆ”ฏๆŒๅคš็งๆฃ€็ดขๆกไปถ๏ผŒ่‡ณๅฐ‘้œ€่ฆๆไพ›ไธ€ไธชๆฃ€็ดขๆกไปถ๏ผŒๅคšไธชๆกไปถๅฏไปฅ็ป„ๅˆไฝฟ็”จใ€‚
20
+
21
+ ๆฃ€็ดขๆกไปถ๏ผˆ่‡ณๅฐ‘ๆไพ›ไธ€ไธช๏ผ‰๏ผš
22
+ - rangeType: ๆŸฅ่ฏข่Œƒๅ›ด๏ผŒๆžšไธพๅ€ผ๏ผšall=ๆŸฅ่ฏขๆ‰€ๆœ‰้—น้’Ÿ๏ผŒnext=ๆŸฅๆ‰พไธ‹ไธ€ไธชๅ“้“ƒ้—น้’Ÿ๏ผŒcurrent=ไธ€ๅฐๆ—ถๅ†…ๆœ€่ฟ‘ไธ€ๆฌกๅขžๆŸฅๆ”น็š„้—น้’Ÿ
23
+ - alarmState: ้—น้’Ÿๅผ€ๅฏ็Šถๆ€๏ผŒ0=ๅ…ณ้—ญ๏ผŒ1=ๅผ€ๅฏ
24
+ - daysOfWakeType: ้—น้’Ÿๅ“้“ƒ็ฑปๅž‹๏ผŒ0=ๅ•ๆฌกๅ“้“ƒ๏ผŒ1=ๆณ•ๅฎš่Š‚ๅ‡ๆ—ฅ๏ผŒ2=ๆฏๅคฉ๏ผŒ3=่‡ชๅฎšไน‰ๆ—ถ้—ด๏ผŒ4=ๆณ•ๅฎšๅทฅไฝœๆ—ฅ
25
+ - startTime: ๆ—ถ้—ด้—ด้š”ๅผ€ๅง‹๏ผŒๆ ผๅผ YYYYMMDD hhmmss๏ผˆไพ‹ๅฆ‚๏ผš20240315 000000๏ผ‰๏ผŒ้œ€่ฆไธŽ endTime ไธ€่ตทไฝฟ็”จ
26
+ - endTime: ๆ—ถ้—ด้—ด้š”็ป“ๆŸ๏ผŒๆ ผๅผ YYYYMMDD hhmmss๏ผˆไพ‹ๅฆ‚๏ผš20240315 235959๏ผ‰๏ผŒ้œ€่ฆไธŽ startTime ไธ€่ตทไฝฟ็”จ
27
+
28
+ ไฝฟ็”จ็คบไพ‹๏ผš
29
+ - ๆŸฅ่ฏขๆ‰€ๆœ‰้—น้’Ÿ๏ผš{"rangeType": "all"}
30
+ - ๆŸฅ่ฏขๅทฒๅผ€ๅฏ็š„้—น้’Ÿ๏ผš{"alarmState": 1}
31
+ - ๆŸฅ่ฏขๆฏๅคฉๅ“้“ƒ็š„้—น้’Ÿ๏ผš{"daysOfWakeType": 2}
32
+ - ๆŸฅ่ฏขๆŸไธชๆ—ถ้—ดๆฎต็š„้—น้’Ÿ๏ผš{"startTime": "20240315 000000", "endTime": "20240315 235959"}
33
+
34
+ ๆณจๆ„๏ผš
35
+ a. ๆ“ไฝœ่ถ…ๆ—ถๆ—ถ้—ดไธบ60็ง’๏ผŒ่ฏทๅ‹ฟ้‡ๅค่ฐƒ็”จๆญคๅทฅๅ…ท๏ผŒๅฆ‚ๆžœ่ถ…ๆ—ถๆˆ–ๅคฑ่ดฅ๏ผŒๆœ€ๅคš้‡่ฏ•ไธ€ๆฌกใ€‚
36
+ b. ไฝฟ็”จ่ฏฅๅทฅๅ…ทไน‹ๅ‰้œ€่Žทๅ–ๅฝ“ๅ‰็œŸๅฎžๆ—ถ้—ด`,
37
+ parameters: {
38
+ type: "object",
39
+ properties: {
40
+ rangeType: {
41
+ type: "string",
42
+ enum: ["all", "next", "current"],
43
+ description: "ๆŸฅ่ฏข่Œƒๅ›ด๏ผšall=ๆ‰€ๆœ‰้—น้’Ÿ๏ผŒnext=ไธ‹ไธ€ไธชๅ“้“ƒ้—น้’Ÿ๏ผŒcurrent=ไธ€ๅฐๆ—ถๅ†…ๆœ€่ฟ‘ไฟฎๆ”น็š„้—น้’Ÿ",
44
+ },
45
+ alarmState: {
46
+ type: "number",
47
+ enum: [0, 1],
48
+ description: "้—น้’Ÿๅผ€ๅฏ็Šถๆ€๏ผš0=ๅ…ณ้—ญ๏ผŒ1=ๅผ€ๅฏ",
49
+ },
50
+ daysOfWakeType: {
51
+ type: "number",
52
+ enum: [0, 1, 2, 3, 4],
53
+ description: "้—น้’Ÿๅ“้“ƒ็ฑปๅž‹๏ผš0=ๅ•ๆฌก๏ผŒ1=ๆณ•ๅฎš่Š‚ๅ‡ๆ—ฅ๏ผŒ2=ๆฏๅคฉ๏ผŒ3=่‡ชๅฎšไน‰๏ผŒ4=ๆณ•ๅฎšๅทฅไฝœๆ—ฅ",
54
+ },
55
+ startTime: {
56
+ type: "string",
57
+ description: "ๆ—ถ้—ด้—ด้š”ๅผ€ๅง‹๏ผŒๆ ผๅผ YYYYMMDD hhmmss๏ผˆไพ‹ๅฆ‚๏ผš20240315 000000๏ผ‰๏ผŒๅฟ…้กปไธŽ endTime ไธ€่ตทไฝฟ็”จ",
58
+ },
59
+ endTime: {
60
+ type: "string",
61
+ description: "ๆ—ถ้—ด้—ด้š”็ป“ๆŸ๏ผŒๆ ผๅผ YYYYMMDD hhmmss๏ผˆไพ‹ๅฆ‚๏ผš20240315 235959๏ผ‰๏ผŒๅฟ…้กปไธŽ startTime ไธ€่ตทไฝฟ็”จ",
62
+ },
63
+ },
64
+ },
65
+ async execute(toolCallId, params) {
66
+ logger.log(`[SEARCH_ALARM_TOOL] ๐Ÿš€ Starting execution`);
67
+ logger.log(`[SEARCH_ALARM_TOOL] - toolCallId: ${toolCallId}`);
68
+ logger.log(`[SEARCH_ALARM_TOOL] - params:`, JSON.stringify(params));
69
+ logger.log(`[SEARCH_ALARM_TOOL] - timestamp: ${new Date().toISOString()}`);
70
+ // ===== Validate at least one search criterion is provided =====
71
+ const hasRangeType = params.rangeType !== undefined && params.rangeType !== null;
72
+ const hasAlarmState = params.alarmState !== undefined && params.alarmState !== null;
73
+ const hasDaysOfWakeType = params.daysOfWakeType !== undefined && params.daysOfWakeType !== null;
74
+ const hasStartTime = params.startTime !== undefined && params.startTime !== null;
75
+ const hasEndTime = params.endTime !== undefined && params.endTime !== null;
76
+ if (!hasRangeType && !hasAlarmState && !hasDaysOfWakeType && !hasStartTime && !hasEndTime) {
77
+ logger.error(`[SEARCH_ALARM_TOOL] โŒ No search criteria provided`);
78
+ throw new Error("่‡ณๅฐ‘้œ€่ฆๆไพ›ไธ€ไธชๆฃ€็ดขๆกไปถ๏ผšrangeTypeใ€alarmStateใ€daysOfWakeType ๆˆ–ๆ—ถ้—ด่Œƒๅ›ด๏ผˆstartTime + endTime๏ผ‰");
79
+ }
80
+ // ===== Validate rangeType =====
81
+ if (hasRangeType) {
82
+ if (typeof params.rangeType !== "string") {
83
+ logger.error(`[SEARCH_ALARM_TOOL] โŒ Invalid rangeType type`);
84
+ throw new Error("rangeType must be a string");
85
+ }
86
+ if (!RANGE_TYPE_VALUES.includes(params.rangeType)) {
87
+ logger.error(`[SEARCH_ALARM_TOOL] โŒ Invalid rangeType value: ${params.rangeType}`);
88
+ throw new Error(`rangeType must be one of: ${RANGE_TYPE_VALUES.join(", ")}`);
89
+ }
90
+ logger.log(`[SEARCH_ALARM_TOOL] - rangeType: ${params.rangeType}`);
91
+ }
92
+ // ===== Validate alarmState =====
93
+ if (hasAlarmState) {
94
+ if (typeof params.alarmState !== "number") {
95
+ logger.error(`[SEARCH_ALARM_TOOL] โŒ Invalid alarmState type`);
96
+ throw new Error("alarmState must be a number");
97
+ }
98
+ if (!ALARM_STATE_VALUES.includes(params.alarmState)) {
99
+ logger.error(`[SEARCH_ALARM_TOOL] โŒ Invalid alarmState value: ${params.alarmState}`);
100
+ throw new Error(`alarmState must be one of: ${ALARM_STATE_VALUES.join(", ")}`);
101
+ }
102
+ logger.log(`[SEARCH_ALARM_TOOL] - alarmState: ${params.alarmState}`);
103
+ }
104
+ // ===== Validate daysOfWakeType =====
105
+ if (hasDaysOfWakeType) {
106
+ if (typeof params.daysOfWakeType !== "number") {
107
+ logger.error(`[SEARCH_ALARM_TOOL] โŒ Invalid daysOfWakeType type`);
108
+ throw new Error("daysOfWakeType must be a number");
109
+ }
110
+ if (!DAYS_OF_WAKE_TYPE_VALUES.includes(params.daysOfWakeType)) {
111
+ logger.error(`[SEARCH_ALARM_TOOL] โŒ Invalid daysOfWakeType value: ${params.daysOfWakeType}`);
112
+ throw new Error(`daysOfWakeType must be one of: ${DAYS_OF_WAKE_TYPE_VALUES.join(", ")}`);
113
+ }
114
+ logger.log(`[SEARCH_ALARM_TOOL] - daysOfWakeType: ${params.daysOfWakeType}`);
115
+ }
116
+ // ===== Validate time interval (startTime and endTime must be provided together) =====
117
+ if (hasStartTime !== hasEndTime) {
118
+ logger.error(`[SEARCH_ALARM_TOOL] โŒ startTime and endTime must be provided together`);
119
+ throw new Error("startTime ๅ’Œ endTime ๅฟ…้กปไธ€่ตทๆไพ›");
120
+ }
121
+ let timeInterval = null;
122
+ if (hasStartTime && hasEndTime) {
123
+ // Parse and convert startTime and endTime to timestamps
124
+ logger.log(`[SEARCH_ALARM_TOOL] ๐Ÿ•’ Parsing time interval...`);
125
+ logger.log(`[SEARCH_ALARM_TOOL] - startTime input: ${params.startTime}`);
126
+ logger.log(`[SEARCH_ALARM_TOOL] - endTime input: ${params.endTime}`);
127
+ const startTimeMs = parseAlarmTimeToTimestamp(params.startTime);
128
+ const endTimeMs = parseAlarmTimeToTimestamp(params.endTime);
129
+ if (startTimeMs === null) {
130
+ logger.error(`[SEARCH_ALARM_TOOL] โŒ Invalid startTime format`);
131
+ throw new Error("Invalid startTime format. Required format: YYYYMMDD hhmmss (e.g., 20240315 000000)");
132
+ }
133
+ if (endTimeMs === null) {
134
+ logger.error(`[SEARCH_ALARM_TOOL] โŒ Invalid endTime format`);
135
+ throw new Error("Invalid endTime format. Required format: YYYYMMDD hhmmss (e.g., 20240315 235959)");
136
+ }
137
+ if (startTimeMs >= endTimeMs) {
138
+ logger.error(`[SEARCH_ALARM_TOOL] โŒ startTime must be before endTime`);
139
+ throw new Error("startTime ๅฟ…้กปๆ—ฉไบŽ endTime");
140
+ }
141
+ timeInterval = [startTimeMs, endTimeMs];
142
+ logger.log(`[SEARCH_ALARM_TOOL] โœ… Time interval parsed: [${startTimeMs}, ${endTimeMs}]`);
143
+ }
144
+ // Get session context
145
+ logger.log(`[SEARCH_ALARM_TOOL] ๐Ÿ” Attempting to get session context...`);
146
+ const sessionContext = getCurrentSessionContext();
147
+ if (!sessionContext) {
148
+ logger.error(`[SEARCH_ALARM_TOOL] โŒ FAILED: No active session found!`);
149
+ logger.error(`[SEARCH_ALARM_TOOL] - toolCallId: ${toolCallId}`);
150
+ throw new Error("No active XY session found. Search alarm tool can only be used during an active conversation.");
151
+ }
152
+ logger.log(`[SEARCH_ALARM_TOOL] โœ… Session context found`);
153
+ logger.log(`[SEARCH_ALARM_TOOL] - sessionId: ${sessionContext.sessionId}`);
154
+ logger.log(`[SEARCH_ALARM_TOOL] - taskId: ${sessionContext.taskId}`);
155
+ logger.log(`[SEARCH_ALARM_TOOL] - messageId: ${sessionContext.messageId}`);
156
+ const { config, sessionId, taskId, messageId } = sessionContext;
157
+ // Get WebSocket manager
158
+ logger.log(`[SEARCH_ALARM_TOOL] ๐Ÿ”Œ Getting WebSocket manager...`);
159
+ const wsManager = getXYWebSocketManager(config);
160
+ logger.log(`[SEARCH_ALARM_TOOL] โœ… WebSocket manager obtained`);
161
+ // Build SearchAlarm command
162
+ logger.log(`[SEARCH_ALARM_TOOL] ๐Ÿ“ฆ Building SearchAlarm command...`);
163
+ // Build intentParam with provided search criteria
164
+ const intentParam = {};
165
+ if (hasRangeType) {
166
+ intentParam.rangeType = params.rangeType;
167
+ }
168
+ if (hasAlarmState) {
169
+ intentParam.alarmState = params.alarmState;
170
+ }
171
+ if (hasDaysOfWakeType) {
172
+ intentParam.daysOfWakeType = params.daysOfWakeType;
173
+ }
174
+ if (timeInterval) {
175
+ intentParam.timeInterval = timeInterval;
176
+ }
177
+ const command = {
178
+ header: {
179
+ namespace: "Common",
180
+ name: "Action",
181
+ },
182
+ payload: {
183
+ cardParam: {},
184
+ executeParam: {
185
+ executeMode: "background",
186
+ intentName: "SearchAlarm",
187
+ bundleName: "com.huawei.hmos.clock",
188
+ needUnlock: true,
189
+ actionResponse: true,
190
+ appType: "OHOS_APP",
191
+ timeOut: 5,
192
+ intentParam,
193
+ permissionId: [],
194
+ achieveType: "INTENT",
195
+ },
196
+ responses: [
197
+ {
198
+ resultCode: "",
199
+ displayText: "",
200
+ ttsText: "",
201
+ },
202
+ ],
203
+ needUploadResult: true,
204
+ noHalfPage: false,
205
+ pageControlRelated: false,
206
+ },
207
+ };
208
+ // Send command and wait for response (60 second timeout)
209
+ logger.log(`[SEARCH_ALARM_TOOL] โณ Setting up promise to wait for alarm search response...`);
210
+ logger.log(`[SEARCH_ALARM_TOOL] - Timeout: 60 seconds`);
211
+ return new Promise((resolve, reject) => {
212
+ const timeout = setTimeout(() => {
213
+ logger.error(`[SEARCH_ALARM_TOOL] โฐ Timeout: No response received within 60 seconds`);
214
+ wsManager.off("data-event", handler);
215
+ reject(new Error("ๆฃ€็ดข้—น้’Ÿ่ถ…ๆ—ถ๏ผˆ60็ง’๏ผ‰"));
216
+ }, 60000);
217
+ // Listen for data events from WebSocket
218
+ const handler = (event) => {
219
+ logger.log(`[SEARCH_ALARM_TOOL] ๐Ÿ“จ Received data event:`, JSON.stringify(event));
220
+ if (event.intentName === "SearchAlarm") {
221
+ logger.log(`[SEARCH_ALARM_TOOL] ๐ŸŽฏ SearchAlarm event received`);
222
+ logger.log(`[SEARCH_ALARM_TOOL] - status: ${event.status}`);
223
+ clearTimeout(timeout);
224
+ wsManager.off("data-event", handler);
225
+ if (event.status === "success" && event.outputs) {
226
+ logger.log(`[SEARCH_ALARM_TOOL] โœ… Alarm search completed successfully`);
227
+ logger.log(`[SEARCH_ALARM_TOOL] - outputs:`, JSON.stringify(event.outputs));
228
+ // Check for error code in outputs
229
+ const code = event.outputs.code !== undefined ? event.outputs.code : null;
230
+ if (code !== null && code !== 0) {
231
+ logger.error(`[SEARCH_ALARM_TOOL] โŒ Device returned error`);
232
+ logger.error(`[SEARCH_ALARM_TOOL] - code: ${code}`);
233
+ const errorMsg = event.outputs.errorMsg || event.outputs.errMsg || "ๆœช็Ÿฅ้”™่ฏฏ";
234
+ logger.error(`[SEARCH_ALARM_TOOL] - errorMsg: ${errorMsg}`);
235
+ reject(new Error(`ๆฃ€็ดข้—น้’Ÿๅคฑ่ดฅ: ${errorMsg} (้”™่ฏฏไปฃ็ : ${code})`));
236
+ return;
237
+ }
238
+ // Extract result.items with safe checks
239
+ const result = event.outputs.result;
240
+ let items = [];
241
+ if (result && typeof result === "object" && Array.isArray(result.items)) {
242
+ items = result.items;
243
+ logger.log(`[SEARCH_ALARM_TOOL] ๐Ÿ“‹ Found ${items.length} alarm(s)`);
244
+ // Parse JSON strings in items array
245
+ // Items are returned as JSON strings that need to be parsed
246
+ const parsedItems = items.map((itemStr, index) => {
247
+ if (typeof itemStr !== "string") {
248
+ logger.warn(`[SEARCH_ALARM_TOOL] โš ๏ธ Item at index ${index} is not a string:`, typeof itemStr);
249
+ return null;
250
+ }
251
+ try {
252
+ const parsed = JSON.parse(itemStr);
253
+ logger.log(`[SEARCH_ALARM_TOOL] ๐Ÿ“‹ Parsed alarm [${index}]:`, JSON.stringify(parsed));
254
+ return parsed;
255
+ }
256
+ catch (parseError) {
257
+ logger.error(`[SEARCH_ALARM_TOOL] โŒ Failed to parse item at index ${index}:`, parseError);
258
+ logger.error(`[SEARCH_ALARM_TOOL] - itemStr: ${itemStr}`);
259
+ return null;
260
+ }
261
+ }).filter((item) => item !== null);
262
+ logger.log(`[SEARCH_ALARM_TOOL] ๐ŸŽ‰ Successfully parsed ${parsedItems.length} alarm(s)`);
263
+ resolve({
264
+ content: [
265
+ {
266
+ type: "text",
267
+ text: JSON.stringify(parsedItems),
268
+ },
269
+ ],
270
+ });
271
+ }
272
+ else {
273
+ logger.warn(`[SEARCH_ALARM_TOOL] โš ๏ธ No items found in result or result is invalid`);
274
+ logger.warn(`[SEARCH_ALARM_TOOL] - result:`, JSON.stringify(result || {}));
275
+ // Return empty array
276
+ resolve({
277
+ content: [
278
+ {
279
+ type: "text",
280
+ text: "[]",
281
+ },
282
+ ],
283
+ });
284
+ }
285
+ }
286
+ else {
287
+ logger.error(`[SEARCH_ALARM_TOOL] โŒ Alarm search failed`);
288
+ logger.error(`[SEARCH_ALARM_TOOL] - status: ${event.status}`);
289
+ logger.error(`[SEARCH_ALARM_TOOL] - outputs:`, JSON.stringify(event.outputs || {}));
290
+ reject(new Error(`ๆฃ€็ดข้—น้’Ÿๅคฑ่ดฅ: ${event.status}`));
291
+ }
292
+ }
293
+ };
294
+ // Register event handler
295
+ logger.log(`[SEARCH_ALARM_TOOL] ๐Ÿ“ก Registering data-event handler on WebSocket manager`);
296
+ wsManager.on("data-event", handler);
297
+ // Send the command
298
+ logger.log(`[SEARCH_ALARM_TOOL] ๐Ÿ“ค Sending SearchAlarm command...`);
299
+ sendCommand({
300
+ config,
301
+ sessionId,
302
+ taskId,
303
+ messageId,
304
+ command,
305
+ })
306
+ .then(() => {
307
+ logger.log(`[SEARCH_ALARM_TOOL] โœ… Command sent successfully, waiting for response...`);
308
+ })
309
+ .catch((error) => {
310
+ logger.error(`[SEARCH_ALARM_TOOL] โŒ Failed to send command:`, error);
311
+ clearTimeout(timeout);
312
+ wsManager.off("data-event", handler);
313
+ reject(error);
314
+ });
315
+ });
316
+ },
317
+ };
318
+ /**
319
+ * Parse alarmTime string (YYYYMMDD hhmmss) to timestamp in milliseconds
320
+ * @param alarmTime - Time string in format "YYYYMMDD hhmmss" (e.g., "20240315 143000")
321
+ * @returns Timestamp in milliseconds, or null if parsing fails
322
+ */
323
+ function parseAlarmTimeToTimestamp(alarmTime) {
324
+ try {
325
+ // Expected format: YYYYMMDD hhmmss
326
+ // Example: 20240315 143000
327
+ const trimmed = alarmTime.trim();
328
+ // Check basic format (should have at least 13 characters: YYYYMMDD hhmmss)
329
+ if (trimmed.length < 13) {
330
+ logger.error(`[SEARCH_ALARM_TOOL] โŒ alarmTime too short: ${trimmed}`);
331
+ return null;
332
+ }
333
+ // Extract date and time parts
334
+ // Format: YYYYMMDD hhmmss
335
+ const datePart = trimmed.substring(0, 8); // YYYYMMDD
336
+ const timePart = trimmed.substring(8).trim(); // hhmmss (may have leading space)
337
+ logger.log(`[SEARCH_ALARM_TOOL] - datePart: ${datePart}, timePart: ${timePart}`);
338
+ // Validate lengths
339
+ if (datePart.length !== 8 || timePart.length !== 6) {
340
+ logger.error(`[SEARCH_ALARM_TOOL] โŒ Invalid part lengths: datePart=${datePart.length}, timePart=${timePart.length}`);
341
+ return null;
342
+ }
343
+ // Parse components
344
+ const year = parseInt(datePart.substring(0, 4), 10);
345
+ const month = parseInt(datePart.substring(4, 6), 10);
346
+ const day = parseInt(datePart.substring(6, 8), 10);
347
+ const hour = parseInt(timePart.substring(0, 2), 10);
348
+ const minute = parseInt(timePart.substring(2, 4), 10);
349
+ const second = parseInt(timePart.substring(4, 6), 10);
350
+ logger.log(`[SEARCH_ALARM_TOOL] - Parsed: ${year}-${month}-${day} ${hour}:${minute}:${second}`);
351
+ // Validate values
352
+ if (isNaN(year) || isNaN(month) || isNaN(day) ||
353
+ isNaN(hour) || isNaN(minute) || isNaN(second)) {
354
+ logger.error(`[SEARCH_ALARM_TOOL] โŒ NaN detected in parsed values`);
355
+ return null;
356
+ }
357
+ // Validate ranges
358
+ if (month < 1 || month > 12) {
359
+ logger.error(`[SEARCH_ALARM_TOOL] โŒ Invalid month: ${month}`);
360
+ return null;
361
+ }
362
+ if (day < 1 || day > 31) {
363
+ logger.error(`[SEARCH_ALARM_TOOL] โŒ Invalid day: ${day}`);
364
+ return null;
365
+ }
366
+ if (hour < 0 || hour > 23) {
367
+ logger.error(`[SEARCH_ALARM_TOOL] โŒ Invalid hour: ${hour}`);
368
+ return null;
369
+ }
370
+ if (minute < 0 || minute > 59) {
371
+ logger.error(`[SEARCH_ALARM_TOOL] โŒ Invalid minute: ${minute}`);
372
+ return null;
373
+ }
374
+ if (second < 0 || second > 59) {
375
+ logger.error(`[SEARCH_ALARM_TOOL] โŒ Invalid second: ${second}`);
376
+ return null;
377
+ }
378
+ // Create Date object and get timestamp
379
+ const date = new Date(year, month - 1, day, hour, minute, second);
380
+ const timestamp = date.getTime();
381
+ if (isNaN(timestamp)) {
382
+ logger.error(`[SEARCH_ALARM_TOOL] โŒ Generated timestamp is NaN`);
383
+ return null;
384
+ }
385
+ return timestamp;
386
+ }
387
+ catch (error) {
388
+ logger.error(`[SEARCH_ALARM_TOOL] โŒ Exception in parseAlarmTimeToTimestamp:`, error);
389
+ return null;
390
+ }
391
+ }
@@ -0,0 +1,12 @@
1
+ /**
2
+ * XY search calendar event tool - searches calendar events on user's device.
3
+ * Returns matching events based on time range and optional title filter.
4
+ *
5
+ * Time range guidelines:
6
+ * - For a specific day: use 00:00:00 to 23:59:59 of that day
7
+ * - For morning: 06:00:00 to 12:00:00
8
+ * - For afternoon: 12:00:00 to 18:00:00
9
+ * - For evening: 18:00:00 to 24:00:00
10
+ * - For a specific time: use ยฑ1 hour range (e.g., for 3PM, use 14:00:00 to 16:00:00)
11
+ */
12
+ export declare const searchCalendarTool: any;