@ynhcj/xiaoyi 0.0.1-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/README.md +207 -0
- package/dist/auth.d.ts +36 -0
- package/dist/auth.js +111 -0
- package/dist/channel.d.ts +189 -0
- package/dist/channel.js +354 -0
- package/dist/config-schema.d.ts +46 -0
- package/dist/config-schema.js +28 -0
- package/dist/file-download.d.ts +17 -0
- package/dist/file-download.js +69 -0
- package/dist/file-handler.d.ts +36 -0
- package/dist/file-handler.js +113 -0
- package/dist/index.d.ts +29 -0
- package/dist/index.js +49 -0
- package/dist/onboarding.d.ts +6 -0
- package/dist/onboarding.js +167 -0
- package/dist/push.d.ts +28 -0
- package/dist/push.js +135 -0
- package/dist/runtime.d.ts +191 -0
- package/dist/runtime.js +438 -0
- package/dist/types.d.ts +280 -0
- package/dist/types.js +8 -0
- package/dist/websocket.d.ts +219 -0
- package/dist/websocket.js +1068 -0
- package/dist/xiaoyi-media.d.ts +81 -0
- package/dist/xiaoyi-media.js +216 -0
- package/dist/xy-bot.d.ts +19 -0
- package/dist/xy-bot.js +277 -0
- package/dist/xy-client.d.ts +26 -0
- package/dist/xy-client.js +78 -0
- package/dist/xy-config.d.ts +18 -0
- package/dist/xy-config.js +37 -0
- package/dist/xy-formatter.d.ts +94 -0
- package/dist/xy-formatter.js +303 -0
- package/dist/xy-monitor.d.ts +17 -0
- package/dist/xy-monitor.js +194 -0
- package/dist/xy-parser.d.ts +49 -0
- package/dist/xy-parser.js +109 -0
- package/dist/xy-reply-dispatcher.d.ts +17 -0
- package/dist/xy-reply-dispatcher.js +308 -0
- package/dist/xy-tools/session-manager.d.ts +29 -0
- package/dist/xy-tools/session-manager.js +80 -0
- package/dist/xy-utils/config-manager.d.ts +26 -0
- package/dist/xy-utils/config-manager.js +61 -0
- package/dist/xy-utils/crypto.d.ts +8 -0
- package/dist/xy-utils/crypto.js +21 -0
- package/dist/xy-utils/logger.d.ts +6 -0
- package/dist/xy-utils/logger.js +37 -0
- package/dist/xy-utils/session.d.ts +34 -0
- package/dist/xy-utils/session.js +55 -0
- package/openclaw.plugin.json +9 -0
- package/package.json +73 -0
- package/xiaoyi.js +1 -0
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import type { OpenClawPluginApi } from "openclaw/plugin-sdk";
|
|
2
|
+
/**
|
|
3
|
+
* XiaoYi Channel Plugin for OpenClaw
|
|
4
|
+
*
|
|
5
|
+
* This plugin enables integration with XiaoYi's A2A protocol via WebSocket.
|
|
6
|
+
* Supports dual server mode for high availability.
|
|
7
|
+
*
|
|
8
|
+
* Configuration example in openclaw.json:
|
|
9
|
+
* {
|
|
10
|
+
* "channels": {
|
|
11
|
+
* "xiaoyi": {
|
|
12
|
+
* "enabled": true,
|
|
13
|
+
* "wsUrl1": "ws://localhost:8765/ws/link",
|
|
14
|
+
* "wsUrl2": "ws://localhost:8766/ws/link",
|
|
15
|
+
* "ak": "test_ak",
|
|
16
|
+
* "sk": "test_sk",
|
|
17
|
+
* "agentId": "your-agent-id"
|
|
18
|
+
* }
|
|
19
|
+
* }
|
|
20
|
+
* }
|
|
21
|
+
*/
|
|
22
|
+
declare const plugin: {
|
|
23
|
+
id: string;
|
|
24
|
+
name: string;
|
|
25
|
+
description: string;
|
|
26
|
+
configSchema: any;
|
|
27
|
+
register(api: OpenClawPluginApi): void;
|
|
28
|
+
};
|
|
29
|
+
export default plugin;
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
const channel_js_1 = require("./channel.js");
|
|
4
|
+
const runtime_js_1 = require("./runtime.js");
|
|
5
|
+
/**
|
|
6
|
+
* XiaoYi Channel Plugin for OpenClaw
|
|
7
|
+
*
|
|
8
|
+
* This plugin enables integration with XiaoYi's A2A protocol via WebSocket.
|
|
9
|
+
* Supports dual server mode for high availability.
|
|
10
|
+
*
|
|
11
|
+
* Configuration example in openclaw.json:
|
|
12
|
+
* {
|
|
13
|
+
* "channels": {
|
|
14
|
+
* "xiaoyi": {
|
|
15
|
+
* "enabled": true,
|
|
16
|
+
* "wsUrl1": "ws://localhost:8765/ws/link",
|
|
17
|
+
* "wsUrl2": "ws://localhost:8766/ws/link",
|
|
18
|
+
* "ak": "test_ak",
|
|
19
|
+
* "sk": "test_sk",
|
|
20
|
+
* "agentId": "your-agent-id"
|
|
21
|
+
* }
|
|
22
|
+
* }
|
|
23
|
+
* }
|
|
24
|
+
*/
|
|
25
|
+
const plugin = {
|
|
26
|
+
id: "xiaoyi",
|
|
27
|
+
name: "XiaoYi Channel",
|
|
28
|
+
description: "XiaoYi channel plugin with A2A protocol support",
|
|
29
|
+
configSchema: undefined,
|
|
30
|
+
register(api) {
|
|
31
|
+
console.log("XiaoYi: register() called - START");
|
|
32
|
+
// Set runtime for managing WebSocket connections
|
|
33
|
+
(0, runtime_js_1.setXiaoYiRuntime)(api.runtime);
|
|
34
|
+
console.log("XiaoYi: setXiaoYiRuntime() completed");
|
|
35
|
+
// Clean up any existing connections from previous plugin loads
|
|
36
|
+
const runtime = require("./runtime.js").getXiaoYiRuntime();
|
|
37
|
+
console.log(`XiaoYi: Got runtime instance: ${runtime.getInstanceId()}, isConnected: ${runtime.isConnected()}`);
|
|
38
|
+
if (runtime.isConnected()) {
|
|
39
|
+
console.log("XiaoYi: Cleaning up existing connection from previous load");
|
|
40
|
+
runtime.stop();
|
|
41
|
+
}
|
|
42
|
+
// Register the channel plugin
|
|
43
|
+
console.log("XiaoYi: About to call registerChannel()");
|
|
44
|
+
api.registerChannel({ plugin: channel_js_1.xiaoyiPlugin });
|
|
45
|
+
console.log("XiaoYi: registerChannel() completed");
|
|
46
|
+
console.log("XiaoYi channel plugin registered - END");
|
|
47
|
+
},
|
|
48
|
+
};
|
|
49
|
+
exports.default = plugin;
|
|
@@ -0,0 +1,167 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* XiaoYi onboarding adapter for CLI setup wizard.
|
|
4
|
+
*/
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.xiaoyiOnboardingAdapter = void 0;
|
|
7
|
+
const channel = "xiaoyi";
|
|
8
|
+
/**
|
|
9
|
+
* Get XiaoYi channel config from OpenClaw config
|
|
10
|
+
*/
|
|
11
|
+
function getXiaoYiConfig(cfg) {
|
|
12
|
+
return cfg?.channels?.xiaoyi;
|
|
13
|
+
}
|
|
14
|
+
/**
|
|
15
|
+
* Check if XiaoYi is properly configured
|
|
16
|
+
*/
|
|
17
|
+
function isXiaoYiConfigured(config) {
|
|
18
|
+
if (!config) {
|
|
19
|
+
return false;
|
|
20
|
+
}
|
|
21
|
+
// Check required fields: ak, sk, agentId
|
|
22
|
+
// wsUrl1/wsUrl2 are optional (defaults will be used if not provided)
|
|
23
|
+
const hasAk = typeof config.ak === "string" && config.ak.trim().length > 0;
|
|
24
|
+
const hasSk = typeof config.sk === "string" && config.sk.trim().length > 0;
|
|
25
|
+
const hasAgentId = typeof config.agentId === "string" && config.agentId.trim().length > 0;
|
|
26
|
+
return hasAk && hasSk && hasAgentId;
|
|
27
|
+
}
|
|
28
|
+
/**
|
|
29
|
+
* Set XiaoYi channel configuration
|
|
30
|
+
*/
|
|
31
|
+
function setXiaoYiConfig(cfg, config) {
|
|
32
|
+
const existing = getXiaoYiConfig(cfg);
|
|
33
|
+
const merged = {
|
|
34
|
+
enabled: config.enabled ?? existing?.enabled ?? true,
|
|
35
|
+
wsUrl: config.wsUrl ?? existing?.wsUrl ?? "",
|
|
36
|
+
wsUrl1: config.wsUrl1 ?? existing?.wsUrl1 ?? "",
|
|
37
|
+
wsUrl2: config.wsUrl2 ?? existing?.wsUrl2 ?? "",
|
|
38
|
+
ak: config.ak ?? existing?.ak ?? "",
|
|
39
|
+
sk: config.sk ?? existing?.sk ?? "",
|
|
40
|
+
agentId: config.agentId ?? existing?.agentId ?? "",
|
|
41
|
+
enableStreaming: config.enableStreaming ?? existing?.enableStreaming ?? true,
|
|
42
|
+
};
|
|
43
|
+
return {
|
|
44
|
+
...cfg,
|
|
45
|
+
channels: {
|
|
46
|
+
...cfg.channels,
|
|
47
|
+
xiaoyi: merged,
|
|
48
|
+
},
|
|
49
|
+
};
|
|
50
|
+
}
|
|
51
|
+
/**
|
|
52
|
+
* Note about XiaoYi setup
|
|
53
|
+
*/
|
|
54
|
+
async function noteXiaoYiSetupHelp(prompter) {
|
|
55
|
+
await prompter.note([
|
|
56
|
+
"XiaoYi (小艺) uses A2A protocol via WebSocket connection.",
|
|
57
|
+
"",
|
|
58
|
+
"Required credentials:",
|
|
59
|
+
" - ak: Access Key for authentication",
|
|
60
|
+
" - sk: Secret Key for authentication",
|
|
61
|
+
" - agentId: Your agent identifier",
|
|
62
|
+
"",
|
|
63
|
+
"WebSocket URLs will use default values.",
|
|
64
|
+
"",
|
|
65
|
+
"Docs: https://docs.openclaw.ai/channels/xiaoyi",
|
|
66
|
+
].join("\n"), "XiaoYi setup");
|
|
67
|
+
}
|
|
68
|
+
/**
|
|
69
|
+
* Prompt for Access Key
|
|
70
|
+
*/
|
|
71
|
+
async function promptAk(prompter, config) {
|
|
72
|
+
const existing = config?.ak ?? "";
|
|
73
|
+
return String(await prompter.text({
|
|
74
|
+
message: "XiaoYi Access Key (ak)",
|
|
75
|
+
initialValue: existing,
|
|
76
|
+
validate: (value) => (value?.trim() ? undefined : "Required"),
|
|
77
|
+
})).trim();
|
|
78
|
+
}
|
|
79
|
+
/**
|
|
80
|
+
* Prompt for Secret Key
|
|
81
|
+
*/
|
|
82
|
+
async function promptSk(prompter, config) {
|
|
83
|
+
const existing = config?.sk ?? "";
|
|
84
|
+
return String(await prompter.text({
|
|
85
|
+
message: "XiaoYi Secret Key (sk)",
|
|
86
|
+
initialValue: existing,
|
|
87
|
+
validate: (value) => (value?.trim() ? undefined : "Required"),
|
|
88
|
+
})).trim();
|
|
89
|
+
}
|
|
90
|
+
/**
|
|
91
|
+
* Prompt for Agent ID
|
|
92
|
+
*/
|
|
93
|
+
async function promptAgentId(prompter, config) {
|
|
94
|
+
const existing = config?.agentId ?? "";
|
|
95
|
+
return String(await prompter.text({
|
|
96
|
+
message: "XiaoYi Agent ID",
|
|
97
|
+
initialValue: existing,
|
|
98
|
+
validate: (value) => (value?.trim() ? undefined : "Required"),
|
|
99
|
+
})).trim();
|
|
100
|
+
}
|
|
101
|
+
exports.xiaoyiOnboardingAdapter = {
|
|
102
|
+
channel,
|
|
103
|
+
getStatus: async ({ cfg }) => {
|
|
104
|
+
const config = getXiaoYiConfig(cfg);
|
|
105
|
+
const configured = isXiaoYiConfigured(config);
|
|
106
|
+
const enabled = config?.enabled !== false;
|
|
107
|
+
const statusLines = [];
|
|
108
|
+
if (configured) {
|
|
109
|
+
statusLines.push(`XiaoYi: ${enabled ? "enabled" : "disabled"}`);
|
|
110
|
+
if (config?.wsUrl1 || config?.wsUrl) {
|
|
111
|
+
statusLines.push(` WebSocket: ${config.wsUrl1 || config.wsUrl}`);
|
|
112
|
+
}
|
|
113
|
+
if (config?.wsUrl2) {
|
|
114
|
+
statusLines.push(` Secondary: ${config.wsUrl2}`);
|
|
115
|
+
}
|
|
116
|
+
if (config?.agentId) {
|
|
117
|
+
statusLines.push(` Agent ID: ${config.agentId}`);
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
else {
|
|
121
|
+
statusLines.push("XiaoYi: needs ak, sk, and agentId");
|
|
122
|
+
}
|
|
123
|
+
return {
|
|
124
|
+
channel,
|
|
125
|
+
configured,
|
|
126
|
+
statusLines,
|
|
127
|
+
selectionHint: configured ? "configured" : "needs setup",
|
|
128
|
+
quickstartScore: 50,
|
|
129
|
+
};
|
|
130
|
+
},
|
|
131
|
+
configure: async ({ cfg, prompter }) => {
|
|
132
|
+
const config = getXiaoYiConfig(cfg);
|
|
133
|
+
if (!isXiaoYiConfigured(config)) {
|
|
134
|
+
await noteXiaoYiSetupHelp(prompter);
|
|
135
|
+
}
|
|
136
|
+
else {
|
|
137
|
+
const reconfigure = await prompter.confirm({
|
|
138
|
+
message: "XiaoYi already configured. Reconfigure?",
|
|
139
|
+
initialValue: false,
|
|
140
|
+
});
|
|
141
|
+
if (!reconfigure) {
|
|
142
|
+
return { cfg, accountId: "default" };
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
// Prompt for required credentials
|
|
146
|
+
const ak = await promptAk(prompter, config);
|
|
147
|
+
const sk = await promptSk(prompter, config);
|
|
148
|
+
const agentId = await promptAgentId(prompter, config);
|
|
149
|
+
const cfgWithConfig = setXiaoYiConfig(cfg, {
|
|
150
|
+
ak,
|
|
151
|
+
sk,
|
|
152
|
+
agentId,
|
|
153
|
+
enabled: true,
|
|
154
|
+
});
|
|
155
|
+
return { cfg: cfgWithConfig, accountId: "default" };
|
|
156
|
+
},
|
|
157
|
+
disable: (cfg) => {
|
|
158
|
+
const xiaoyi = getXiaoYiConfig(cfg);
|
|
159
|
+
return {
|
|
160
|
+
...cfg,
|
|
161
|
+
channels: {
|
|
162
|
+
...cfg.channels,
|
|
163
|
+
xiaoyi: { ...xiaoyi, enabled: false },
|
|
164
|
+
},
|
|
165
|
+
};
|
|
166
|
+
},
|
|
167
|
+
};
|
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;
|
|
@@ -0,0 +1,191 @@
|
|
|
1
|
+
import { XiaoYiWebSocketManager } from "./websocket.js";
|
|
2
|
+
import { XiaoYiChannelConfig } from "./types.js";
|
|
3
|
+
/**
|
|
4
|
+
* Timeout configuration
|
|
5
|
+
*/
|
|
6
|
+
export interface TimeoutConfig {
|
|
7
|
+
enabled: boolean;
|
|
8
|
+
duration: number;
|
|
9
|
+
message: string;
|
|
10
|
+
}
|
|
11
|
+
/**
|
|
12
|
+
* Runtime state for XiaoYi channel
|
|
13
|
+
* Manages single WebSocket connection (single account mode)
|
|
14
|
+
*/
|
|
15
|
+
export declare class XiaoYiRuntime {
|
|
16
|
+
private connection;
|
|
17
|
+
private pluginRuntime;
|
|
18
|
+
private config;
|
|
19
|
+
private sessionToTaskIdMap;
|
|
20
|
+
private instanceId;
|
|
21
|
+
private sessionTimeoutMap;
|
|
22
|
+
private sessionTimeoutSent;
|
|
23
|
+
private timeoutConfig;
|
|
24
|
+
private sessionAbortControllerMap;
|
|
25
|
+
private sessionActiveRunMap;
|
|
26
|
+
private sessionStartTimeMap;
|
|
27
|
+
private static readonly SESSION_STALE_TIMEOUT_MS;
|
|
28
|
+
private sessionTaskTimeoutMap;
|
|
29
|
+
private sessionPushPendingMap;
|
|
30
|
+
private taskTimeoutMs;
|
|
31
|
+
constructor();
|
|
32
|
+
getInstanceId(): string;
|
|
33
|
+
/**
|
|
34
|
+
* Set OpenClaw PluginRuntime (from api.runtime in register())
|
|
35
|
+
*/
|
|
36
|
+
setPluginRuntime(runtime: any): void;
|
|
37
|
+
/**
|
|
38
|
+
* Get OpenClaw PluginRuntime
|
|
39
|
+
*/
|
|
40
|
+
getPluginRuntime(): any;
|
|
41
|
+
/**
|
|
42
|
+
* Start connection (single account mode)
|
|
43
|
+
*/
|
|
44
|
+
start(config: XiaoYiChannelConfig): Promise<void>;
|
|
45
|
+
/**
|
|
46
|
+
* Stop connection
|
|
47
|
+
*/
|
|
48
|
+
stop(): void;
|
|
49
|
+
/**
|
|
50
|
+
* Set timeout configuration
|
|
51
|
+
*/
|
|
52
|
+
setTimeoutConfig(config: Partial<TimeoutConfig>): void;
|
|
53
|
+
/**
|
|
54
|
+
* Get timeout configuration
|
|
55
|
+
*/
|
|
56
|
+
getTimeoutConfig(): TimeoutConfig;
|
|
57
|
+
/**
|
|
58
|
+
* Set timeout for a session
|
|
59
|
+
* @param sessionId - Session ID
|
|
60
|
+
* @param callback - Function to call when timeout occurs
|
|
61
|
+
* @returns The interval ID (for cancellation)
|
|
62
|
+
*
|
|
63
|
+
* IMPORTANT: This now uses setInterval instead of setTimeout
|
|
64
|
+
* - First trigger: after 60 seconds
|
|
65
|
+
* - Subsequent triggers: every 60 seconds after that
|
|
66
|
+
* - Cleared when: response received, session completed, or explicitly cleared
|
|
67
|
+
*/
|
|
68
|
+
setTimeoutForSession(sessionId: string, callback: () => void): NodeJS.Timeout | undefined;
|
|
69
|
+
/**
|
|
70
|
+
* Clear timeout interval for a session
|
|
71
|
+
* @param sessionId - Session ID
|
|
72
|
+
*/
|
|
73
|
+
clearSessionTimeout(sessionId: string): void;
|
|
74
|
+
/**
|
|
75
|
+
* Check if timeout has been sent for a session
|
|
76
|
+
* @param sessionId - Session ID
|
|
77
|
+
*/
|
|
78
|
+
isSessionTimeout(sessionId: string): boolean;
|
|
79
|
+
/**
|
|
80
|
+
* Mark session as completed (clear timeout and timeout flag)
|
|
81
|
+
* @param sessionId - Session ID
|
|
82
|
+
*/
|
|
83
|
+
markSessionCompleted(sessionId: string): void;
|
|
84
|
+
/**
|
|
85
|
+
* Clear all timeout intervals
|
|
86
|
+
*/
|
|
87
|
+
clearAllTimeouts(): void;
|
|
88
|
+
/**
|
|
89
|
+
* Get WebSocket manager
|
|
90
|
+
*/
|
|
91
|
+
getConnection(): XiaoYiWebSocketManager | null;
|
|
92
|
+
/**
|
|
93
|
+
* Check if connected
|
|
94
|
+
*/
|
|
95
|
+
isConnected(): boolean;
|
|
96
|
+
/**
|
|
97
|
+
* Get configuration
|
|
98
|
+
*/
|
|
99
|
+
getConfig(): XiaoYiChannelConfig | null;
|
|
100
|
+
/**
|
|
101
|
+
* Set taskId for a session
|
|
102
|
+
*/
|
|
103
|
+
setTaskIdForSession(sessionId: string, taskId: string): void;
|
|
104
|
+
/**
|
|
105
|
+
* Get taskId for a session
|
|
106
|
+
*/
|
|
107
|
+
getTaskIdForSession(sessionId: string): string | undefined;
|
|
108
|
+
/**
|
|
109
|
+
* Clear taskId for a session
|
|
110
|
+
*/
|
|
111
|
+
clearTaskIdForSession(sessionId: string): void;
|
|
112
|
+
/**
|
|
113
|
+
* Create and register an AbortController for a session
|
|
114
|
+
* @param sessionId - Session ID
|
|
115
|
+
* @returns The AbortController and its signal, or null if session is busy
|
|
116
|
+
*/
|
|
117
|
+
createAbortControllerForSession(sessionId: string): {
|
|
118
|
+
controller: AbortController;
|
|
119
|
+
signal: AbortSignal;
|
|
120
|
+
} | null;
|
|
121
|
+
/**
|
|
122
|
+
* Check if a session has an active agent run
|
|
123
|
+
* If session is active but stale (超过 SESSION_STALE_TIMEOUT_MS), automatically clean up
|
|
124
|
+
* @param sessionId - Session ID
|
|
125
|
+
* @returns true if session is busy
|
|
126
|
+
*/
|
|
127
|
+
isSessionActive(sessionId: string): boolean;
|
|
128
|
+
/**
|
|
129
|
+
* Abort a session's agent run
|
|
130
|
+
* @param sessionId - Session ID
|
|
131
|
+
* @returns true if a controller was found and aborted, false otherwise
|
|
132
|
+
*/
|
|
133
|
+
abortSession(sessionId: string): boolean;
|
|
134
|
+
/**
|
|
135
|
+
* Check if a session has been aborted
|
|
136
|
+
* @param sessionId - Session ID
|
|
137
|
+
* @returns true if the session's abort signal was triggered
|
|
138
|
+
*/
|
|
139
|
+
isSessionAborted(sessionId: string): boolean;
|
|
140
|
+
/**
|
|
141
|
+
* Clear the AbortController for a session (call when agent completes successfully)
|
|
142
|
+
* @param sessionId - Session ID
|
|
143
|
+
*/
|
|
144
|
+
clearAbortControllerForSession(sessionId: string): void;
|
|
145
|
+
/**
|
|
146
|
+
* Clear all AbortControllers
|
|
147
|
+
*/
|
|
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;
|
|
189
|
+
}
|
|
190
|
+
export declare function getXiaoYiRuntime(): XiaoYiRuntime;
|
|
191
|
+
export declare function setXiaoYiRuntime(runtime: any): void;
|