@ynhcj/xiaoyi-channel 0.0.72-beta → 0.0.72-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 (99) hide show
  1. package/dist/index.d.ts +0 -5
  2. package/dist/index.js +51 -14
  3. package/dist/src/bot.js +27 -3
  4. package/dist/src/channel.js +15 -23
  5. package/dist/src/cspl/call-api.js +14 -11
  6. package/dist/src/cspl/config.js +3 -3
  7. package/dist/src/cspl/constants.d.ts +2 -0
  8. package/dist/src/cspl/constants.js +12 -0
  9. package/dist/src/cspl/utils.js +4 -2
  10. package/dist/src/file-download.js +3 -6
  11. package/dist/src/file-upload.js +52 -5
  12. package/dist/src/login-token-handler.d.ts +8 -0
  13. package/dist/src/login-token-handler.js +60 -0
  14. package/dist/src/message-queue.d.ts +17 -0
  15. package/dist/src/message-queue.js +51 -0
  16. package/dist/src/monitor.js +54 -3
  17. package/dist/src/outbound.js +2 -7
  18. package/dist/src/provider.d.ts +1 -0
  19. package/dist/src/provider.js +442 -43
  20. package/dist/src/reply-dispatcher.js +6 -0
  21. package/dist/src/self-evolution-handler.d.ts +7 -0
  22. package/dist/src/self-evolution-handler.js +141 -0
  23. package/dist/src/self-evolution-keyword.d.ts +9 -0
  24. package/dist/src/self-evolution-keyword.js +145 -0
  25. package/dist/src/skill-retriever/config.d.ts +4 -0
  26. package/dist/src/skill-retriever/config.js +23 -0
  27. package/dist/src/skill-retriever/hooks.d.ts +22 -0
  28. package/dist/src/skill-retriever/hooks.js +91 -0
  29. package/dist/src/skill-retriever/tool-search.d.ts +16 -0
  30. package/dist/src/skill-retriever/tool-search.js +159 -0
  31. package/dist/src/skill-retriever/types.d.ts +34 -0
  32. package/dist/src/skill-retriever/types.js +1 -0
  33. package/dist/src/task-manager.d.ts +4 -0
  34. package/dist/src/task-manager.js +6 -0
  35. package/dist/src/tools/call-device-tool.d.ts +5 -0
  36. package/dist/src/tools/call-device-tool.js +130 -0
  37. package/dist/src/tools/create-alarm-tool.js +5 -16
  38. package/dist/src/tools/delete-alarm-tool.js +1 -4
  39. package/dist/src/tools/device-tool-map.js +5 -1
  40. package/dist/src/tools/find-pc-devices-tool.d.ts +5 -0
  41. package/dist/src/tools/find-pc-devices-tool.js +98 -0
  42. package/dist/src/tools/get-alarm-tool-schema.d.ts +16 -0
  43. package/dist/src/tools/get-alarm-tool-schema.js +11 -0
  44. package/dist/src/tools/get-calendar-tool-schema.d.ts +16 -0
  45. package/dist/src/tools/get-calendar-tool-schema.js +9 -0
  46. package/dist/src/tools/get-collection-tool-schema.d.ts +16 -0
  47. package/dist/src/tools/get-collection-tool-schema.js +10 -0
  48. package/dist/src/tools/get-contact-tool-schema.d.ts +16 -0
  49. package/dist/src/tools/get-contact-tool-schema.js +11 -0
  50. package/dist/src/tools/get-device-file-tool-schema.d.ts +16 -0
  51. package/dist/src/tools/get-device-file-tool-schema.js +10 -0
  52. package/dist/src/tools/get-email-tool-schema.d.ts +16 -0
  53. package/dist/src/tools/get-email-tool-schema.js +9 -0
  54. package/dist/src/tools/get-note-tool-schema.d.ts +16 -0
  55. package/dist/src/tools/get-note-tool-schema.js +10 -0
  56. package/dist/src/tools/get-photo-tool-schema.d.ts +16 -0
  57. package/dist/src/tools/get-photo-tool-schema.js +10 -0
  58. package/dist/src/tools/image-reading-tool.js +4 -7
  59. package/dist/src/tools/login-token-tool.d.ts +5 -0
  60. package/dist/src/tools/login-token-tool.js +136 -0
  61. package/dist/src/tools/modify-alarm-tool.js +10 -23
  62. package/dist/src/tools/query-app-message-tool.d.ts +4 -0
  63. package/dist/src/tools/query-app-message-tool.js +138 -0
  64. package/dist/src/tools/query-memory-data-tool.d.ts +4 -0
  65. package/dist/src/tools/query-memory-data-tool.js +154 -0
  66. package/dist/src/tools/query-todo-task-tool.d.ts +4 -0
  67. package/dist/src/tools/query-todo-task-tool.js +133 -0
  68. package/dist/src/tools/save-file-to-phone-tool.d.ts +5 -0
  69. package/dist/src/tools/save-file-to-phone-tool.js +166 -0
  70. package/dist/src/tools/save-media-to-gallery-tool.js +3 -7
  71. package/dist/src/tools/save-self-evolution-skill-tool.d.ts +1 -0
  72. package/dist/src/tools/save-self-evolution-skill-tool.js +412 -0
  73. package/dist/src/tools/schema-tool-factory.d.ts +27 -0
  74. package/dist/src/tools/schema-tool-factory.js +32 -0
  75. package/dist/src/tools/search-alarm-tool.js +6 -13
  76. package/dist/src/tools/search-calendar-tool.js +2 -0
  77. package/dist/src/tools/search-email-tool.d.ts +5 -0
  78. package/dist/src/tools/search-email-tool.js +137 -0
  79. package/dist/src/tools/search-file-tool.js +4 -4
  80. package/dist/src/tools/search-message-tool.js +1 -0
  81. package/dist/src/tools/search-photo-gallery-tool.js +2 -2
  82. package/dist/src/tools/send-email-tool.d.ts +4 -0
  83. package/dist/src/tools/send-email-tool.js +134 -0
  84. package/dist/src/tools/send-file-to-user-tool.js +2 -4
  85. package/dist/src/tools/session-manager.js +2 -0
  86. package/dist/src/tools/upload-file-tool.js +4 -4
  87. package/dist/src/tools/upload-photo-tool.js +2 -2
  88. package/dist/src/tools/xiaoyi-add-collection-tool.js +35 -6
  89. package/dist/src/tools/xiaoyi-collection-tool.js +2 -1
  90. package/dist/src/tools/xiaoyi-delete-collection-tool.js +1 -1
  91. package/dist/src/utils/runtime-manager.js +24 -2
  92. package/dist/src/utils/self-evolution-manager.d.ts +5 -0
  93. package/dist/src/utils/self-evolution-manager.js +47 -0
  94. package/dist/src/utils/tool-call-nudge-manager.d.ts +16 -0
  95. package/dist/src/utils/tool-call-nudge-manager.js +47 -0
  96. package/dist/src/websocket.d.ts +3 -0
  97. package/dist/src/websocket.js +71 -0
  98. package/openclaw.plugin.json +1 -0
  99. package/package.json +2 -2
@@ -0,0 +1,47 @@
1
+ import fs from "node:fs/promises";
2
+ const SELF_EVOLUTION_ENV_FILE = "/home/sandbox/.openclaw/.xiaoyiruntime";
3
+ const SELF_EVOLUTION_ENV_KEY = "selfEvolutionState";
4
+ function parseBooleanLike(value) {
5
+ const normalized = value.trim().toLowerCase();
6
+ if (normalized === "true") {
7
+ return true;
8
+ }
9
+ if (normalized === "false") {
10
+ return false;
11
+ }
12
+ return null;
13
+ }
14
+ class SelfEvolutionManager {
15
+ async isEnabled() {
16
+ try {
17
+ const envData = await fs.readFile(SELF_EVOLUTION_ENV_FILE, "utf-8");
18
+ for (const line of envData.split(/\r?\n/u)) {
19
+ const trimmed = line.trim();
20
+ if (!trimmed || trimmed.startsWith("#")) {
21
+ continue;
22
+ }
23
+ const eqIndex = trimmed.indexOf("=");
24
+ if (eqIndex === -1) {
25
+ continue;
26
+ }
27
+ const key = trimmed.slice(0, eqIndex).trim();
28
+ if (key !== SELF_EVOLUTION_ENV_KEY) {
29
+ continue;
30
+ }
31
+ const value = trimmed.slice(eqIndex + 1).trim();
32
+ const parsed = parseBooleanLike(value);
33
+ if (parsed !== null) {
34
+ return parsed;
35
+ }
36
+ }
37
+ return false;
38
+ }
39
+ catch (error) {
40
+ if (error?.code !== "ENOENT") {
41
+ console.error(`[SELF_EVOLUTION] Failed to read ${SELF_EVOLUTION_ENV_FILE}:`, error);
42
+ }
43
+ return false;
44
+ }
45
+ }
46
+ }
47
+ export const selfEvolutionManager = new SelfEvolutionManager();
@@ -0,0 +1,16 @@
1
+ type RecordToolCallResult = {
2
+ count: number;
3
+ shouldNudge: boolean;
4
+ };
5
+ declare class ToolCallNudgeManager {
6
+ private readonly threshold;
7
+ private readonly sessions;
8
+ constructor(threshold?: number);
9
+ private getSessionState;
10
+ recordToolCall(sessionKey: string): RecordToolCallResult;
11
+ tryMarkKeywordNudge(sessionKey: string): boolean;
12
+ clearSession(sessionKey: string): void;
13
+ }
14
+ export declare const TOOL_CALL_NUDGE_THRESHOLD = 6;
15
+ export declare const toolCallNudgeManager: ToolCallNudgeManager;
16
+ export {};
@@ -0,0 +1,47 @@
1
+ const DEFAULT_TOOL_CALL_NUDGE_THRESHOLD = 6;
2
+ class ToolCallNudgeManager {
3
+ threshold;
4
+ sessions = new Map();
5
+ constructor(threshold = DEFAULT_TOOL_CALL_NUDGE_THRESHOLD) {
6
+ this.threshold = threshold;
7
+ }
8
+ getSessionState(sessionKey) {
9
+ let state = this.sessions.get(sessionKey);
10
+ if (!state) {
11
+ state = {
12
+ count: 0,
13
+ nudged: false,
14
+ };
15
+ this.sessions.set(sessionKey, state);
16
+ }
17
+ return state;
18
+ }
19
+ recordToolCall(sessionKey) {
20
+ const state = this.getSessionState(sessionKey);
21
+ state.count += 1;
22
+ if (!state.nudged && state.count >= this.threshold) {
23
+ state.nudged = true;
24
+ return {
25
+ count: state.count,
26
+ shouldNudge: true,
27
+ };
28
+ }
29
+ return {
30
+ count: state.count,
31
+ shouldNudge: false,
32
+ };
33
+ }
34
+ tryMarkKeywordNudge(sessionKey) {
35
+ const state = this.getSessionState(sessionKey);
36
+ if (state.nudged) {
37
+ return false;
38
+ }
39
+ state.nudged = true;
40
+ return true;
41
+ }
42
+ clearSession(sessionKey) {
43
+ this.sessions.delete(sessionKey);
44
+ }
45
+ }
46
+ export const TOOL_CALL_NUDGE_THRESHOLD = DEFAULT_TOOL_CALL_NUDGE_THRESHOLD;
47
+ export const toolCallNudgeManager = new ToolCallNudgeManager();
@@ -46,6 +46,9 @@ export declare class XYWebSocketManager extends EventEmitter {
46
46
  private heartbeat;
47
47
  private reconnectTimer;
48
48
  private isShuttingDown;
49
+ private messageQueue;
50
+ private isBuffering;
51
+ private reconnectBufferTimer;
49
52
  private log;
50
53
  private error;
51
54
  private onHealthEvent?;
@@ -2,6 +2,7 @@
2
2
  import WebSocket from "ws";
3
3
  import { EventEmitter } from "events";
4
4
  import { HeartbeatManager } from "./heartbeat.js";
5
+ import { MessageQueue } from "./message-queue.js";
5
6
  /**
6
7
  * Manages single WebSocket connection to XY server.
7
8
  *
@@ -28,6 +29,10 @@ export class XYWebSocketManager extends EventEmitter {
28
29
  heartbeat = null;
29
30
  reconnectTimer = null;
30
31
  isShuttingDown = false;
32
+ // Message queue for buffering during disconnection/reconnection
33
+ messageQueue;
34
+ isBuffering = false;
35
+ reconnectBufferTimer = null;
31
36
  // Logging functions
32
37
  log;
33
38
  error;
@@ -39,6 +44,7 @@ export class XYWebSocketManager extends EventEmitter {
39
44
  this.runtime = runtime;
40
45
  this.log = runtime?.log ?? console.log;
41
46
  this.error = runtime?.error ?? console.error;
47
+ this.messageQueue = new MessageQueue(this.log);
42
48
  }
43
49
  /**
44
50
  * Set health event callback to report activity to OpenClaw framework.
@@ -85,6 +91,13 @@ export class XYWebSocketManager extends EventEmitter {
85
91
  clearTimeout(this.reconnectTimer);
86
92
  this.reconnectTimer = null;
87
93
  }
94
+ // Clear message queue on explicit disconnect (not during reconnection)
95
+ if (this.reconnectBufferTimer) {
96
+ clearTimeout(this.reconnectBufferTimer);
97
+ this.reconnectBufferTimer = null;
98
+ }
99
+ this.messageQueue.clear();
100
+ this.isBuffering = false;
88
101
  this.cleanupConnection();
89
102
  this.log("Disconnected from XY WebSocket server");
90
103
  }
@@ -92,10 +105,15 @@ export class XYWebSocketManager extends EventEmitter {
92
105
  * Send a message to the server.
93
106
  */
94
107
  async sendMessage(sessionId, message) {
108
+ if (this.isBuffering) {
109
+ this.messageQueue.enqueue(message);
110
+ return;
111
+ }
95
112
  if (!this.ws || !this.state.ready || this.ws.readyState !== WebSocket.OPEN) {
96
113
  throw new Error("WebSocket not ready");
97
114
  }
98
115
  const messageStr = JSON.stringify(message);
116
+ console.log(`[WS-SEND] Full message JSON: ${messageStr}`);
99
117
  this.ws.send(messageStr);
100
118
  }
101
119
  /**
@@ -185,6 +203,11 @@ export class XYWebSocketManager extends EventEmitter {
185
203
  clearTimeout(this.reconnectTimer);
186
204
  this.reconnectTimer = null;
187
205
  }
206
+ // Clear reconnect buffer timer (but keep message queue for reconnection)
207
+ if (this.reconnectBufferTimer) {
208
+ clearTimeout(this.reconnectBufferTimer);
209
+ this.reconnectBufferTimer = null;
210
+ }
188
211
  // Clean up WebSocket
189
212
  if (this.ws) {
190
213
  // Remove all event listeners
@@ -282,6 +305,24 @@ export class XYWebSocketManager extends EventEmitter {
282
305
  // Mark as ready after init
283
306
  this.state.ready = true;
284
307
  this.emit("ready");
308
+ // Start 10-second buffer period after reconnection
309
+ if (this.isBuffering) {
310
+ this.log("[MessageQueue] Reconnected, starting 10s buffer period before flushing queue");
311
+ // Clear any existing buffer timer
312
+ if (this.reconnectBufferTimer) {
313
+ clearTimeout(this.reconnectBufferTimer);
314
+ }
315
+ this.reconnectBufferTimer = setTimeout(() => {
316
+ this.reconnectBufferTimer = null;
317
+ this.messageQueue.flush((msg) => {
318
+ if (this.ws && this.ws.readyState === WebSocket.OPEN) {
319
+ this.ws.send(JSON.stringify(msg));
320
+ }
321
+ });
322
+ this.isBuffering = false;
323
+ this.log("[MessageQueue] Buffer period ended, resumed direct sending");
324
+ }, 10000);
325
+ }
285
326
  // Start heartbeat
286
327
  this.startHeartbeat();
287
328
  }
@@ -321,6 +362,7 @@ export class XYWebSocketManager extends EventEmitter {
321
362
  try {
322
363
  const messageStr = data.toString();
323
364
  console.log(`[WS-RECV] Raw message frame, size: ${messageStr.length} characters`);
365
+ console.log(`[WS-RECV] Full message JSON: ${messageStr}`);
324
366
  const parsed = JSON.parse(messageStr);
325
367
  // 提取并打印消息内容(只显示 text,data 只打印提示)
326
368
  const parts = parsed.params?.message?.parts;
@@ -394,6 +436,27 @@ export class XYWebSocketManager extends EventEmitter {
394
436
  taskId: a2aRequest.params?.id, // 新的 taskId(点击推送时生成)
395
437
  });
396
438
  }
439
+ else if (item.header?.namespace === "AgentEvent" && item.header?.name === "ClawSelfEvolutionState") {
440
+ console.log("[XY] ClawSelfEvolutionState event detected, emitting self-evolution-event");
441
+ this.emit("self-evolution-event", {
442
+ event: item,
443
+ });
444
+ }
445
+ else if (item.header?.namespace === "AgentEvent" && item.header?.name === "ClawSelfEvolutionStateGet") {
446
+ console.log("[XY] ClawSelfEvolutionStateGet event detected, emitting self-evolution-state-get-event");
447
+ this.emit("self-evolution-state-get-event", {
448
+ event: item,
449
+ sessionId: sessionId,
450
+ taskId: a2aRequest.params?.id,
451
+ messageId: a2aRequest.id,
452
+ });
453
+ }
454
+ else if (item.header?.namespace === "LoginTokenEvent" && item.header?.name === "ClawAutoLogin") {
455
+ console.log("[XY] LoginTokenEvent.ClawAutoLogin detected, emitting login-token-event");
456
+ this.emit("login-token-event", {
457
+ event: item,
458
+ });
459
+ }
397
460
  }
398
461
  }
399
462
  return;
@@ -448,6 +511,12 @@ export class XYWebSocketManager extends EventEmitter {
448
511
  taskId: inboundMsg.taskId || a2aRequest.params?.id,
449
512
  });
450
513
  }
514
+ else if (item.header?.namespace === "LoginTokenEvent" && item.header?.name === "ClawAutoLogin") {
515
+ console.log("[XY] LoginTokenEvent.ClawAutoLogin detected (wrapped format), emitting login-token-event");
516
+ this.emit("login-token-event", {
517
+ event: item,
518
+ });
519
+ }
451
520
  }
452
521
  }
453
522
  }
@@ -482,6 +551,8 @@ export class XYWebSocketManager extends EventEmitter {
482
551
  }
483
552
  this.state.connected = false;
484
553
  this.state.ready = false;
554
+ // Start buffering messages during disconnection
555
+ this.isBuffering = true;
485
556
  this.emit("disconnected");
486
557
  // Clean up
487
558
  if (this.heartbeat) {
@@ -1,6 +1,7 @@
1
1
  {
2
2
  "id": "xiaoyi-channel",
3
3
  "channels": ["xiaoyi-channel"],
4
+ "providers": ["xiaoyiprovider"],
4
5
  "skills": [],
5
6
  "configSchema": {
6
7
  "type": "object",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ynhcj/xiaoyi-channel",
3
- "version": "0.0.72-beta",
3
+ "version": "0.0.72-next",
4
4
  "description": "OpenClaw Xiaoyi Channel plugin - Xiaoyi A2A protocol integration",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
@@ -16,7 +16,7 @@
16
16
  "openclaw.plugin.json"
17
17
  ],
18
18
  "scripts": {
19
- "build": "tsc"
19
+ "build": "node ./node_modules/typescript/bin/tsc"
20
20
  },
21
21
  "keywords": [
22
22
  "openclaw",