@ynhcj/xiaoyi 2.5.5 → 2.5.7

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 (43) hide show
  1. package/dist/auth.d.ts +1 -1
  2. package/dist/channel.d.ts +116 -14
  3. package/dist/channel.js +199 -665
  4. package/dist/config-schema.d.ts +8 -8
  5. package/dist/config-schema.js +5 -5
  6. package/dist/file-download.d.ts +17 -0
  7. package/dist/file-download.js +69 -0
  8. package/dist/heartbeat.d.ts +39 -0
  9. package/dist/heartbeat.js +102 -0
  10. package/dist/index.d.ts +1 -4
  11. package/dist/index.js +7 -11
  12. package/dist/push.d.ts +28 -0
  13. package/dist/push.js +135 -0
  14. package/dist/runtime.d.ts +48 -2
  15. package/dist/runtime.js +117 -3
  16. package/dist/types.d.ts +95 -1
  17. package/dist/websocket.d.ts +49 -1
  18. package/dist/websocket.js +279 -20
  19. package/dist/xy-bot.d.ts +19 -0
  20. package/dist/xy-bot.js +277 -0
  21. package/dist/xy-client.d.ts +26 -0
  22. package/dist/xy-client.js +78 -0
  23. package/dist/xy-config.d.ts +18 -0
  24. package/dist/xy-config.js +37 -0
  25. package/dist/xy-formatter.d.ts +94 -0
  26. package/dist/xy-formatter.js +303 -0
  27. package/dist/xy-monitor.d.ts +17 -0
  28. package/dist/xy-monitor.js +187 -0
  29. package/dist/xy-parser.d.ts +49 -0
  30. package/dist/xy-parser.js +109 -0
  31. package/dist/xy-reply-dispatcher.d.ts +17 -0
  32. package/dist/xy-reply-dispatcher.js +308 -0
  33. package/dist/xy-tools/session-manager.d.ts +29 -0
  34. package/dist/xy-tools/session-manager.js +80 -0
  35. package/dist/xy-utils/config-manager.d.ts +26 -0
  36. package/dist/xy-utils/config-manager.js +61 -0
  37. package/dist/xy-utils/crypto.d.ts +8 -0
  38. package/dist/xy-utils/crypto.js +21 -0
  39. package/dist/xy-utils/logger.d.ts +6 -0
  40. package/dist/xy-utils/logger.js +37 -0
  41. package/dist/xy-utils/session.d.ts +34 -0
  42. package/dist/xy-utils/session.js +55 -0
  43. package/package.json +32 -16
@@ -8,8 +8,10 @@ export declare const XiaoYiConfigSchema: z.ZodObject<{
8
8
  name: z.ZodOptional<z.ZodString>;
9
9
  /** Whether this channel is enabled */
10
10
  enabled: z.ZodDefault<z.ZodOptional<z.ZodBoolean>>;
11
- /** WebSocket URL for A2A connection */
12
- wsUrl: z.ZodOptional<z.ZodString>;
11
+ /** First WebSocket server URL */
12
+ wsUrl1: z.ZodDefault<z.ZodOptional<z.ZodString>>;
13
+ /** Second WebSocket server URL */
14
+ wsUrl2: z.ZodDefault<z.ZodOptional<z.ZodString>>;
13
15
  /** Access Key for authentication */
14
16
  ak: z.ZodOptional<z.ZodString>;
15
17
  /** Secret Key for authentication */
@@ -18,27 +20,25 @@ export declare const XiaoYiConfigSchema: z.ZodObject<{
18
20
  agentId: z.ZodOptional<z.ZodString>;
19
21
  /** Enable debug logging */
20
22
  debug: z.ZodDefault<z.ZodOptional<z.ZodBoolean>>;
21
- /** Enable streaming responses (default: false) */
22
- enableStreaming: z.ZodDefault<z.ZodOptional<z.ZodBoolean>>;
23
23
  /** Multi-account configuration */
24
24
  accounts: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodUnknown>>;
25
25
  }, "strip", z.ZodTypeAny, {
26
26
  enabled?: boolean;
27
- wsUrl?: string;
27
+ wsUrl1?: string;
28
+ wsUrl2?: string;
28
29
  ak?: string;
29
30
  sk?: string;
30
31
  agentId?: string;
31
- enableStreaming?: boolean;
32
32
  name?: string;
33
33
  debug?: boolean;
34
34
  accounts?: Record<string, unknown>;
35
35
  }, {
36
36
  enabled?: boolean;
37
- wsUrl?: string;
37
+ wsUrl1?: string;
38
+ wsUrl2?: string;
38
39
  ak?: string;
39
40
  sk?: string;
40
41
  agentId?: string;
41
- enableStreaming?: boolean;
42
42
  name?: string;
43
43
  debug?: boolean;
44
44
  accounts?: Record<string, unknown>;
@@ -10,9 +10,11 @@ exports.XiaoYiConfigSchema = zod_1.z.object({
10
10
  /** Account name (optional display name) */
11
11
  name: zod_1.z.string().optional(),
12
12
  /** Whether this channel is enabled */
13
- enabled: zod_1.z.boolean().optional().default(true),
14
- /** WebSocket URL for A2A connection */
15
- wsUrl: zod_1.z.string().optional(),
13
+ enabled: zod_1.z.boolean().optional().default(false),
14
+ /** First WebSocket server URL */
15
+ wsUrl1: zod_1.z.string().optional().default("wss://hag.cloud.huawei.com/openclaw/v1/ws/link"),
16
+ /** Second WebSocket server URL */
17
+ wsUrl2: zod_1.z.string().optional().default("wss://116.63.174.231/openclaw/v1/ws/link"),
16
18
  /** Access Key for authentication */
17
19
  ak: zod_1.z.string().optional(),
18
20
  /** Secret Key for authentication */
@@ -21,8 +23,6 @@ exports.XiaoYiConfigSchema = zod_1.z.object({
21
23
  agentId: zod_1.z.string().optional(),
22
24
  /** Enable debug logging */
23
25
  debug: zod_1.z.boolean().optional().default(false),
24
- /** Enable streaming responses (default: false) */
25
- enableStreaming: zod_1.z.boolean().optional().default(false),
26
26
  /** Multi-account configuration */
27
27
  accounts: zod_1.z.record(zod_1.z.string(), zod_1.z.unknown()).optional(),
28
28
  });
@@ -0,0 +1,17 @@
1
+ /**
2
+ * Download a file from URL to local path.
3
+ */
4
+ export declare function downloadFile(url: string, destPath: string): Promise<void>;
5
+ /**
6
+ * Download files from A2A file parts.
7
+ * Returns array of local file paths.
8
+ */
9
+ export declare function downloadFilesFromParts(fileParts: Array<{
10
+ name: string;
11
+ mimeType: string;
12
+ uri: string;
13
+ }>, tempDir?: string): Promise<Array<{
14
+ path: string;
15
+ name: string;
16
+ mimeType: string;
17
+ }>>;
@@ -0,0 +1,69 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.downloadFile = downloadFile;
7
+ exports.downloadFilesFromParts = downloadFilesFromParts;
8
+ // File download utilities
9
+ const node_fetch_1 = __importDefault(require("node-fetch"));
10
+ const promises_1 = __importDefault(require("fs/promises"));
11
+ const path_1 = __importDefault(require("path"));
12
+ const logger_js_1 = require("./xy-utils/logger.js");
13
+ /**
14
+ * Download a file from URL to local path.
15
+ */
16
+ async function downloadFile(url, destPath) {
17
+ logger_js_1.logger.debug(`Downloading file from ${url} to ${destPath}`);
18
+ const controller = new AbortController();
19
+ const timeout = setTimeout(() => controller.abort(), 30000); // 30 seconds timeout
20
+ try {
21
+ const response = await (0, node_fetch_1.default)(url, { signal: controller.signal });
22
+ if (!response.ok) {
23
+ throw new Error(`HTTP ${response.status}: ${response.statusText}`);
24
+ }
25
+ const arrayBuffer = await response.arrayBuffer();
26
+ const buffer = Buffer.from(arrayBuffer);
27
+ await promises_1.default.writeFile(destPath, buffer);
28
+ logger_js_1.logger.debug(`File downloaded successfully: ${destPath}`);
29
+ }
30
+ catch (error) {
31
+ if (error.name === 'AbortError') {
32
+ logger_js_1.logger.error(`Download timeout (30s) for ${url}`);
33
+ throw new Error(`Download timeout after 30 seconds`);
34
+ }
35
+ logger_js_1.logger.error(`Failed to download file from ${url}:`, error);
36
+ throw error;
37
+ }
38
+ finally {
39
+ clearTimeout(timeout);
40
+ }
41
+ }
42
+ /**
43
+ * Download files from A2A file parts.
44
+ * Returns array of local file paths.
45
+ */
46
+ async function downloadFilesFromParts(fileParts, tempDir = "/tmp/xy_channel") {
47
+ // Create temp directory if it doesn't exist
48
+ await promises_1.default.mkdir(tempDir, { recursive: true });
49
+ const downloadedFiles = [];
50
+ for (const filePart of fileParts) {
51
+ const { name, mimeType, uri } = filePart;
52
+ // Generate safe file name
53
+ const safeName = name.replace(/[^a-zA-Z0-9._-]/g, "_");
54
+ const destPath = path_1.default.join(tempDir, `${Date.now()}_${safeName}`);
55
+ try {
56
+ await downloadFile(uri, destPath);
57
+ downloadedFiles.push({
58
+ path: destPath,
59
+ name,
60
+ mimeType,
61
+ });
62
+ }
63
+ catch (error) {
64
+ logger_js_1.logger.error(`Failed to download file ${name}:`, error);
65
+ // Continue with other files
66
+ }
67
+ }
68
+ return downloadedFiles;
69
+ }
@@ -0,0 +1,39 @@
1
+ import WebSocket from "ws";
2
+ export interface HeartbeatConfig {
3
+ interval: number;
4
+ timeout: number;
5
+ message: string;
6
+ }
7
+ /**
8
+ * Manages heartbeat for a WebSocket connection.
9
+ * Supports both application-level and protocol-level heartbeats.
10
+ */
11
+ export declare class HeartbeatManager {
12
+ private ws;
13
+ private config;
14
+ private onTimeout;
15
+ private serverName;
16
+ private onHeartbeatSuccess?;
17
+ private intervalTimer;
18
+ private timeoutTimer;
19
+ private lastPongTime;
20
+ private log;
21
+ private error;
22
+ constructor(ws: WebSocket, config: HeartbeatConfig, onTimeout: () => void, serverName?: string, logFn?: (msg: string, ...args: any[]) => void, errorFn?: (msg: string, ...args: any[]) => void, onHeartbeatSuccess?: () => void);
23
+ /**
24
+ * Start heartbeat monitoring.
25
+ */
26
+ start(): void;
27
+ /**
28
+ * Stop heartbeat monitoring.
29
+ */
30
+ stop(): void;
31
+ /**
32
+ * Send a heartbeat ping.
33
+ */
34
+ private sendHeartbeat;
35
+ /**
36
+ * Check if connection is healthy based on last pong time.
37
+ */
38
+ isHealthy(): boolean;
39
+ }
@@ -0,0 +1,102 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.HeartbeatManager = void 0;
7
+ // Heartbeat management for WebSocket connections
8
+ const ws_1 = __importDefault(require("ws"));
9
+ /**
10
+ * Manages heartbeat for a WebSocket connection.
11
+ * Supports both application-level and protocol-level heartbeats.
12
+ */
13
+ class HeartbeatManager {
14
+ constructor(ws, config, onTimeout, serverName = "unknown", logFn, errorFn, onHeartbeatSuccess // ✅ 心跳成功回调,向 OpenClaw 报告健康状态
15
+ ) {
16
+ this.ws = ws;
17
+ this.config = config;
18
+ this.onTimeout = onTimeout;
19
+ this.serverName = serverName;
20
+ this.onHeartbeatSuccess = onHeartbeatSuccess;
21
+ this.intervalTimer = null;
22
+ this.timeoutTimer = null;
23
+ this.lastPongTime = 0;
24
+ this.log = logFn ?? console.log;
25
+ this.error = errorFn ?? console.error;
26
+ }
27
+ /**
28
+ * Start heartbeat monitoring.
29
+ */
30
+ start() {
31
+ this.stop(); // Clear any existing timers
32
+ this.lastPongTime = Date.now();
33
+ // Setup ping/pong for protocol-level heartbeat
34
+ this.ws.on("pong", () => {
35
+ this.lastPongTime = Date.now();
36
+ if (this.timeoutTimer) {
37
+ clearTimeout(this.timeoutTimer);
38
+ this.timeoutTimer = null;
39
+ }
40
+ // ✅ Report health: heartbeat successful - notify OpenClaw framework
41
+ this.onHeartbeatSuccess?.();
42
+ this.log(`[${this.serverName}] Heartbeat pong received, health reported to OpenClaw`);
43
+ });
44
+ // Start interval timer
45
+ this.intervalTimer = setInterval(() => {
46
+ this.sendHeartbeat();
47
+ }, this.config.interval);
48
+ this.log(`[${this.serverName}] Heartbeat started: interval=${this.config.interval}ms, timeout=${this.config.timeout}ms`);
49
+ }
50
+ /**
51
+ * Stop heartbeat monitoring.
52
+ */
53
+ stop() {
54
+ if (this.intervalTimer) {
55
+ clearInterval(this.intervalTimer);
56
+ this.intervalTimer = null;
57
+ }
58
+ if (this.timeoutTimer) {
59
+ clearTimeout(this.timeoutTimer);
60
+ this.timeoutTimer = null;
61
+ }
62
+ // Remove pong listener
63
+ this.ws.off("pong", () => { });
64
+ this.log(`[${this.serverName}] Heartbeat stopped`);
65
+ }
66
+ /**
67
+ * Send a heartbeat ping.
68
+ */
69
+ sendHeartbeat() {
70
+ if (this.ws.readyState !== ws_1.default.OPEN) {
71
+ console.warn(`[${this.serverName}] Cannot send heartbeat: WebSocket not open`);
72
+ return;
73
+ }
74
+ try {
75
+ // Send application-level heartbeat message
76
+ this.log(`[${this.serverName}] Sending heartbeat frame:`, this.config.message.substring(0, 100));
77
+ this.ws.send(this.config.message);
78
+ // Send protocol-level ping
79
+ this.ws.ping();
80
+ this.log(`[${this.serverName}] Protocol-level ping sent`);
81
+ // Setup timeout timer
82
+ this.timeoutTimer = setTimeout(() => {
83
+ this.error(`[${this.serverName}] Heartbeat timeout - no pong received`);
84
+ this.onTimeout();
85
+ }, this.config.timeout);
86
+ }
87
+ catch (error) {
88
+ this.error(`[${this.serverName}] Failed to send heartbeat:`, error);
89
+ }
90
+ }
91
+ /**
92
+ * Check if connection is healthy based on last pong time.
93
+ */
94
+ isHealthy() {
95
+ if (this.lastPongTime === 0) {
96
+ return true; // Not started yet
97
+ }
98
+ const timeSinceLastPong = Date.now() - this.lastPongTime;
99
+ return timeSinceLastPong < this.config.interval + this.config.timeout;
100
+ }
101
+ }
102
+ exports.HeartbeatManager = HeartbeatManager;
package/dist/index.d.ts CHANGED
@@ -14,13 +14,10 @@ import type { OpenClawPluginApi } from "openclaw/plugin-sdk";
14
14
  * "wsUrl2": "ws://localhost:8766/ws/link",
15
15
  * "ak": "test_ak",
16
16
  * "sk": "test_sk",
17
- * "agentId": "your-agent-id",
18
- * "enableStreaming": true
17
+ * "agentId": "your-agent-id"
19
18
  * }
20
19
  * }
21
20
  * }
22
- *
23
- * Backward compatibility: Can use "wsUrl" instead of "wsUrl1" (wsUrl2 will use default)
24
21
  */
25
22
  declare const plugin: {
26
23
  id: string;
package/dist/index.js CHANGED
@@ -1,8 +1,7 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- const plugin_sdk_1 = require("openclaw/plugin-sdk");
4
- const channel_1 = require("./channel");
5
- const runtime_1 = require("./runtime");
3
+ const channel_js_1 = require("./channel.js");
4
+ const runtime_js_1 = require("./runtime.js");
6
5
  /**
7
6
  * XiaoYi Channel Plugin for OpenClaw
8
7
  *
@@ -18,26 +17,23 @@ const runtime_1 = require("./runtime");
18
17
  * "wsUrl2": "ws://localhost:8766/ws/link",
19
18
  * "ak": "test_ak",
20
19
  * "sk": "test_sk",
21
- * "agentId": "your-agent-id",
22
- * "enableStreaming": true
20
+ * "agentId": "your-agent-id"
23
21
  * }
24
22
  * }
25
23
  * }
26
- *
27
- * Backward compatibility: Can use "wsUrl" instead of "wsUrl1" (wsUrl2 will use default)
28
24
  */
29
25
  const plugin = {
30
26
  id: "xiaoyi",
31
27
  name: "XiaoYi Channel",
32
28
  description: "XiaoYi channel plugin with A2A protocol support",
33
- configSchema: (0, plugin_sdk_1.emptyPluginConfigSchema)(),
29
+ configSchema: undefined,
34
30
  register(api) {
35
31
  console.log("XiaoYi: register() called - START");
36
32
  // Set runtime for managing WebSocket connections
37
- (0, runtime_1.setXiaoYiRuntime)(api.runtime);
33
+ (0, runtime_js_1.setXiaoYiRuntime)(api.runtime);
38
34
  console.log("XiaoYi: setXiaoYiRuntime() completed");
39
35
  // Clean up any existing connections from previous plugin loads
40
- const runtime = require("./runtime").getXiaoYiRuntime();
36
+ const runtime = require("./runtime.js").getXiaoYiRuntime();
41
37
  console.log(`XiaoYi: Got runtime instance: ${runtime.getInstanceId()}, isConnected: ${runtime.isConnected()}`);
42
38
  if (runtime.isConnected()) {
43
39
  console.log("XiaoYi: Cleaning up existing connection from previous load");
@@ -45,7 +41,7 @@ const plugin = {
45
41
  }
46
42
  // Register the channel plugin
47
43
  console.log("XiaoYi: About to call registerChannel()");
48
- api.registerChannel({ plugin: channel_1.xiaoyiPlugin });
44
+ api.registerChannel({ plugin: channel_js_1.xiaoyiPlugin });
49
45
  console.log("XiaoYi: registerChannel() completed");
50
46
  console.log("XiaoYi channel plugin registered - END");
51
47
  },
package/dist/push.d.ts ADDED
@@ -0,0 +1,28 @@
1
+ import { XiaoYiChannelConfig } from "./types.js";
2
+ /**
3
+ * Push message sending service
4
+ * Sends notifications to XiaoYi clients via webhook API
5
+ */
6
+ export declare class XiaoYiPushService {
7
+ private config;
8
+ private readonly pushUrl;
9
+ constructor(config: XiaoYiChannelConfig);
10
+ /**
11
+ * Check if push functionality is configured
12
+ */
13
+ isConfigured(): boolean;
14
+ /**
15
+ * Generate HMAC-SHA256 signature
16
+ */
17
+ private generateSignature;
18
+ /**
19
+ * Generate UUID
20
+ */
21
+ private generateUUID;
22
+ /**
23
+ * Send push notification (with summary text)
24
+ * @param text - Summary text to send (e.g., first 30 characters)
25
+ * @param pushText - Push notification message (e.g., "任务已完成:xxx...")
26
+ */
27
+ sendPush(text: string, pushText: string): Promise<boolean>;
28
+ }
package/dist/push.js ADDED
@@ -0,0 +1,135 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || (function () {
19
+ var ownKeys = function(o) {
20
+ ownKeys = Object.getOwnPropertyNames || function (o) {
21
+ var ar = [];
22
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
+ return ar;
24
+ };
25
+ return ownKeys(o);
26
+ };
27
+ return function (mod) {
28
+ if (mod && mod.__esModule) return mod;
29
+ var result = {};
30
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31
+ __setModuleDefault(result, mod);
32
+ return result;
33
+ };
34
+ })();
35
+ Object.defineProperty(exports, "__esModule", { value: true });
36
+ exports.XiaoYiPushService = void 0;
37
+ const crypto = __importStar(require("crypto"));
38
+ /**
39
+ * Push message sending service
40
+ * Sends notifications to XiaoYi clients via webhook API
41
+ */
42
+ class XiaoYiPushService {
43
+ constructor(config) {
44
+ this.pushUrl = "https://hag.cloud.huawei.com/open-ability-agent/v1/agent-webhook";
45
+ this.config = config;
46
+ }
47
+ /**
48
+ * Check if push functionality is configured
49
+ */
50
+ isConfigured() {
51
+ return Boolean(this.config.apiId?.trim() &&
52
+ this.config.pushId?.trim() &&
53
+ this.config.ak?.trim() &&
54
+ this.config.sk?.trim());
55
+ }
56
+ /**
57
+ * Generate HMAC-SHA256 signature
58
+ */
59
+ generateSignature(timestamp) {
60
+ const hmac = crypto.createHmac("sha256", this.config.sk);
61
+ hmac.update(timestamp);
62
+ return hmac.digest().toString("base64");
63
+ }
64
+ /**
65
+ * Generate UUID
66
+ */
67
+ generateUUID() {
68
+ return "xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx".replace(/[xy]/g, (c) => {
69
+ const r = (Math.random() * 16) | 0;
70
+ const v = c === "x" ? r : (r & 0x3) | 0x8;
71
+ return v.toString(16);
72
+ });
73
+ }
74
+ /**
75
+ * Send push notification (with summary text)
76
+ * @param text - Summary text to send (e.g., first 30 characters)
77
+ * @param pushText - Push notification message (e.g., "任务已完成:xxx...")
78
+ */
79
+ async sendPush(text, pushText) {
80
+ if (!this.isConfigured()) {
81
+ console.log("[PUSH] Push not configured, skipping");
82
+ return false;
83
+ }
84
+ try {
85
+ const timestamp = Date.now().toString();
86
+ const signature = this.generateSignature(timestamp);
87
+ const messageId = this.generateUUID();
88
+ const payload = {
89
+ jsonrpc: "2.0",
90
+ id: messageId,
91
+ result: {
92
+ id: this.generateUUID(),
93
+ apiId: this.config.apiId,
94
+ pushId: this.config.pushId,
95
+ pushText: pushText,
96
+ kind: "task",
97
+ artifacts: [{
98
+ artifactId: this.generateUUID(),
99
+ parts: [{
100
+ kind: "text",
101
+ text: text, // Summary text
102
+ }]
103
+ }],
104
+ status: { state: "completed" }
105
+ }
106
+ };
107
+ console.log(`[PUSH] Sending push notification: ${pushText}`);
108
+ const response = await fetch(this.pushUrl, {
109
+ method: "POST",
110
+ headers: {
111
+ "Content-Type": "application/json",
112
+ "Accept": "application/json",
113
+ "x-hag-trace-id": this.generateUUID(),
114
+ "X-Access-Key": this.config.ak,
115
+ "X-Sign": signature,
116
+ "X-Ts": timestamp,
117
+ },
118
+ body: JSON.stringify(payload),
119
+ });
120
+ if (response.ok) {
121
+ console.log("[PUSH] Push notification sent successfully");
122
+ return true;
123
+ }
124
+ else {
125
+ console.error(`[PUSH] Failed: HTTP ${response.status}`);
126
+ return false;
127
+ }
128
+ }
129
+ catch (error) {
130
+ console.error("[PUSH] Error:", error);
131
+ return false;
132
+ }
133
+ }
134
+ }
135
+ exports.XiaoYiPushService = XiaoYiPushService;
package/dist/runtime.d.ts CHANGED
@@ -1,5 +1,5 @@
1
- import { XiaoYiWebSocketManager } from "./websocket";
2
- import { XiaoYiChannelConfig } from "./types";
1
+ import { XiaoYiWebSocketManager } from "./websocket.js";
2
+ import { XiaoYiChannelConfig } from "./types.js";
3
3
  /**
4
4
  * Timeout configuration
5
5
  */
@@ -23,6 +23,11 @@ export declare class XiaoYiRuntime {
23
23
  private timeoutConfig;
24
24
  private sessionAbortControllerMap;
25
25
  private sessionActiveRunMap;
26
+ private sessionStartTimeMap;
27
+ private static readonly SESSION_STALE_TIMEOUT_MS;
28
+ private sessionTaskTimeoutMap;
29
+ private sessionPushPendingMap;
30
+ private taskTimeoutMs;
26
31
  constructor();
27
32
  getInstanceId(): string;
28
33
  /**
@@ -115,6 +120,7 @@ export declare class XiaoYiRuntime {
115
120
  } | null;
116
121
  /**
117
122
  * Check if a session has an active agent run
123
+ * If session is active but stale (超过 SESSION_STALE_TIMEOUT_MS), automatically clean up
118
124
  * @param sessionId - Session ID
119
125
  * @returns true if session is busy
120
126
  */
@@ -140,6 +146,46 @@ export declare class XiaoYiRuntime {
140
146
  * Clear all AbortControllers
141
147
  */
142
148
  clearAllAbortControllers(): void;
149
+ /**
150
+ * Generate a composite key for session+task combination
151
+ * This ensures each task has its own push state, even within the same session
152
+ */
153
+ private getPushStateKey;
154
+ /**
155
+ * Set task timeout time (from configuration)
156
+ */
157
+ setTaskTimeout(timeoutMs: number): void;
158
+ /**
159
+ * Set a 1-hour task timeout timer for a session
160
+ * @returns timeout ID
161
+ */
162
+ setTaskTimeoutForSession(sessionId: string, taskId: string, callback: (sessionId: string, taskId: string) => void): NodeJS.Timeout;
163
+ /**
164
+ * Clear the task timeout timer for a session
165
+ */
166
+ clearTaskTimeoutForSession(sessionId: string): void;
167
+ /**
168
+ * Check if session+task is waiting for push notification
169
+ * @param sessionId - Session ID
170
+ * @param taskId - Task ID (optional, for per-task tracking)
171
+ */
172
+ isSessionWaitingForPush(sessionId: string, taskId?: string): boolean;
173
+ /**
174
+ * Mark session+task as waiting for push notification
175
+ * @param sessionId - Session ID
176
+ * @param taskId - Task ID (optional, for per-task tracking)
177
+ */
178
+ markSessionWaitingForPush(sessionId: string, taskId?: string): void;
179
+ /**
180
+ * Clear the waiting push state for a session+task
181
+ * @param sessionId - Session ID
182
+ * @param taskId - Task ID (optional, for per-task tracking)
183
+ */
184
+ clearSessionWaitingForPush(sessionId: string, taskId?: string): void;
185
+ /**
186
+ * Clear all task timeout related state for a session
187
+ */
188
+ clearTaskTimeoutState(sessionId: string): void;
143
189
  }
144
190
  export declare function getXiaoYiRuntime(): XiaoYiRuntime;
145
191
  export declare function setXiaoYiRuntime(runtime: any): void;