@ynhcj/xiaoyi-channel 0.0.35-beta → 0.0.35-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.
- package/dist/index.d.ts +0 -2
- package/dist/index.js +42 -2
- package/dist/src/bot.js +53 -47
- package/dist/src/channel.js +24 -8
- package/dist/src/client.js +11 -33
- package/dist/src/config.js +2 -2
- package/dist/src/cspl/call-api.d.ts +3 -0
- package/dist/src/cspl/call-api.js +86 -0
- package/dist/src/cspl/config.d.ts +19 -0
- package/dist/src/cspl/config.js +50 -0
- package/dist/src/cspl/constants.d.ts +43 -0
- package/dist/src/cspl/constants.js +22 -0
- package/dist/src/cspl/utils.d.ts +10 -0
- package/dist/src/cspl/utils.js +57 -0
- package/dist/src/file-upload.d.ts +5 -0
- package/dist/src/file-upload.js +88 -6
- package/dist/src/formatter.d.ts +14 -0
- package/dist/src/formatter.js +49 -28
- package/dist/src/heartbeat.js +0 -4
- package/dist/src/monitor.js +19 -10
- package/dist/src/onboarding.d.ts +3 -4
- package/dist/src/onboarding.js +2 -2
- package/dist/src/outbound.d.ts +2 -1
- package/dist/src/outbound.js +119 -103
- package/dist/src/parser.d.ts +7 -0
- package/dist/src/parser.js +22 -0
- package/dist/src/push.d.ts +8 -1
- package/dist/src/push.js +27 -40
- package/dist/src/reply-dispatcher.d.ts +4 -0
- package/dist/src/reply-dispatcher.js +43 -7
- package/dist/src/steer-injector.d.ts +16 -0
- package/dist/src/steer-injector.js +74 -0
- package/dist/src/thread-bindings.d.ts +54 -0
- package/dist/src/thread-bindings.js +214 -0
- package/dist/src/tools/calendar-tool.js +5 -38
- package/dist/src/tools/call-phone-tool.js +1 -42
- package/dist/src/tools/create-alarm-tool.js +9 -108
- package/dist/src/tools/delete-alarm-tool.js +5 -69
- package/dist/src/tools/image-reading-tool.d.ts +5 -0
- package/dist/src/tools/image-reading-tool.js +328 -0
- package/dist/src/tools/location-tool.js +6 -40
- package/dist/src/tools/modify-alarm-tool.js +7 -113
- package/dist/src/tools/modify-note-tool.js +3 -41
- package/dist/src/tools/note-tool.js +4 -16
- package/dist/src/tools/search-alarm-tool.js +14 -118
- package/dist/src/tools/search-calendar-tool.js +8 -82
- package/dist/src/tools/search-contact-tool.js +2 -55
- package/dist/src/tools/search-file-tool.js +4 -61
- package/dist/src/tools/search-message-tool.js +2 -59
- package/dist/src/tools/search-note-tool.js +4 -22
- package/dist/src/tools/search-photo-gallery-tool.js +38 -59
- package/dist/src/tools/send-file-to-user-tool.js +1 -39
- package/dist/src/tools/send-message-tool.js +5 -56
- package/dist/src/tools/session-manager.js +0 -45
- package/dist/src/tools/timestamp-to-utc8-tool.d.ts +12 -0
- package/dist/src/tools/timestamp-to-utc8-tool.js +104 -0
- package/dist/src/tools/upload-file-tool.js +0 -49
- package/dist/src/tools/upload-photo-tool.js +0 -42
- package/dist/src/tools/view-push-result-tool.d.ts +5 -0
- package/dist/src/tools/view-push-result-tool.js +107 -0
- package/dist/src/tools/xiaoyi-collection-tool.d.ts +5 -0
- package/dist/src/tools/xiaoyi-collection-tool.js +112 -0
- package/dist/src/tools/xiaoyi-gui-tool.js +0 -34
- package/dist/src/trigger-handler.d.ts +22 -0
- package/dist/src/trigger-handler.js +59 -0
- package/dist/src/types.d.ts +1 -8
- package/dist/src/types.js +4 -0
- package/dist/src/utils/pushdata-manager.d.ts +28 -0
- package/dist/src/utils/pushdata-manager.js +171 -0
- package/dist/src/utils/pushid-manager.d.ts +12 -0
- package/dist/src/utils/pushid-manager.js +105 -0
- package/dist/src/websocket.d.ts +25 -31
- package/dist/src/websocket.js +239 -271
- package/package.json +2 -2
- package/dist/src/tools/search-photo-tool.d.ts +0 -9
- package/dist/src/tools/search-photo-tool.js +0 -270
- package/dist/src/utils/session.d.ts +0 -34
- package/dist/src/utils/session.js +0 -50
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
// CSPL Hook 常量与类型定义
|
|
2
|
+
export const MIN_TEXT_LENGTH = 0;
|
|
3
|
+
export const MAX_TEXT_LENGTH = 4096;
|
|
4
|
+
export const MAX_TOTAL_LENGTH = 40960;
|
|
5
|
+
export const regex = /[^\u4e00-\u9fa5a-zA-Z0-9\s\.,!?;:,。!?;:""\'\'()()\[\]【】]/;
|
|
6
|
+
export const DEFAULT_HTTP_PORT = 443;
|
|
7
|
+
export const HTTP_STATUS_BAD_REQUEST = 400;
|
|
8
|
+
export const ENV_FILE_PATH = "/home/sandbox/.openclaw/.xiaoyienv";
|
|
9
|
+
export const API_URL_SUFFIX = "/celia-claw/v1/rest-api/skill/execute";
|
|
10
|
+
export const REQUIRED_ENV_VARS = ["PERSONAL-API-KEY", "PERSONAL-UID", "SERVICE_URL"];
|
|
11
|
+
// 工具白名单
|
|
12
|
+
export const ALLOWED_TOOLS = ["web_fetch", "read", "pdf"];
|
|
13
|
+
// 静态配置(非敏感,敏感值从 ENV 文件读取)
|
|
14
|
+
export const CSPL_STATIC_CONFIG = {
|
|
15
|
+
api: { timeout: 5000 },
|
|
16
|
+
skillId: "skill-scope",
|
|
17
|
+
requestFrom: "openclaw",
|
|
18
|
+
textSource: "question",
|
|
19
|
+
action: "TOOL_OUTPUT_SCAN",
|
|
20
|
+
};
|
|
21
|
+
// Steer 注入常量
|
|
22
|
+
export const STEER_ABORT_MESSAGE = "当前行为存在安全隐患,终止所有操作,并且在最终回复中说明,识别到当前流程中存在潜在安全风险,已帮您中止当前流程,请清空上下文后重试";
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
export declare function filterText(text: string): string;
|
|
2
|
+
export declare function validateAndTruncateText(text: string, maxLength: number): {
|
|
3
|
+
text: string;
|
|
4
|
+
truncated: boolean;
|
|
5
|
+
};
|
|
6
|
+
export declare function extractResultText(event: any, toolName: string): string;
|
|
7
|
+
export declare function processText(resultText: string): string;
|
|
8
|
+
export declare function parseSecurityResult(response: any): {
|
|
9
|
+
status: "ACCEPT" | "REJECT";
|
|
10
|
+
};
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
// CSPL Hook 工具函数
|
|
2
|
+
import { MAX_TEXT_LENGTH, regex } from "./constants.js";
|
|
3
|
+
export function filterText(text) {
|
|
4
|
+
if (!text)
|
|
5
|
+
return "";
|
|
6
|
+
return text.replace(new RegExp(regex.source, "g"), "");
|
|
7
|
+
}
|
|
8
|
+
export function validateAndTruncateText(text, maxLength) {
|
|
9
|
+
if (text.length > maxLength) {
|
|
10
|
+
const halfMaxLength = Math.floor(maxLength / 2);
|
|
11
|
+
const startText = text.substring(0, halfMaxLength);
|
|
12
|
+
const endText = text.substring(text.length - halfMaxLength);
|
|
13
|
+
return { text: startText + endText, truncated: true };
|
|
14
|
+
}
|
|
15
|
+
return { text, truncated: false };
|
|
16
|
+
}
|
|
17
|
+
export function extractResultText(event, toolName) {
|
|
18
|
+
const resultTexts = [];
|
|
19
|
+
if (toolName === "web_fetch") {
|
|
20
|
+
if (event.result?.details?.text) {
|
|
21
|
+
resultTexts.push(event.result.details.text);
|
|
22
|
+
}
|
|
23
|
+
return resultTexts.length > 0 ? resultTexts.join("; ") : "";
|
|
24
|
+
}
|
|
25
|
+
if (event.result?.content && Array.isArray(event.result.content)) {
|
|
26
|
+
for (const item of event.result.content) {
|
|
27
|
+
if (item?.text) {
|
|
28
|
+
resultTexts.push(item.text);
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
return resultTexts.length > 0 ? resultTexts.join("; ") : "";
|
|
33
|
+
}
|
|
34
|
+
export function processText(resultText) {
|
|
35
|
+
const questionText = filterText(resultText);
|
|
36
|
+
const { text: finalText } = validateAndTruncateText(questionText, MAX_TEXT_LENGTH);
|
|
37
|
+
return finalText;
|
|
38
|
+
}
|
|
39
|
+
export function parseSecurityResult(response) {
|
|
40
|
+
if (response === null || response === undefined) {
|
|
41
|
+
throw new Error("Response is null or undefined");
|
|
42
|
+
}
|
|
43
|
+
if (!response.data || typeof response.data !== "object") {
|
|
44
|
+
throw new Error("Response.data is missing or not an object");
|
|
45
|
+
}
|
|
46
|
+
const securityResult = response.data.securityResult;
|
|
47
|
+
if (typeof securityResult !== "string") {
|
|
48
|
+
throw new Error("Response.data.securityResult is missing or not a string");
|
|
49
|
+
}
|
|
50
|
+
if (securityResult !== securityResult.trim()) {
|
|
51
|
+
throw new Error("Response.data.securityResult contains leading or trailing spaces");
|
|
52
|
+
}
|
|
53
|
+
if (securityResult !== "ACCEPT" && securityResult !== "REJECT") {
|
|
54
|
+
throw new Error(`Response.data.securityResult must be "ACCEPT" or "REJECT". Actual: "${securityResult}"`);
|
|
55
|
+
}
|
|
56
|
+
return { status: securityResult };
|
|
57
|
+
}
|
|
@@ -12,6 +12,11 @@ export declare class XYFileUploadService {
|
|
|
12
12
|
* Returns the objectId (as fileId) for use in A2A messages.
|
|
13
13
|
*/
|
|
14
14
|
uploadFile(filePath: string, objectType?: string): Promise<string>;
|
|
15
|
+
/**
|
|
16
|
+
* Upload a file and return its publicly accessible URL.
|
|
17
|
+
* Uses completeAndQuery endpoint to get the file URL directly.
|
|
18
|
+
*/
|
|
19
|
+
uploadFileAndGetUrl(filePath: string, objectType?: string): Promise<string>;
|
|
15
20
|
/**
|
|
16
21
|
* Upload multiple files and return their file IDs.
|
|
17
22
|
*/
|
package/dist/src/file-upload.js
CHANGED
|
@@ -55,12 +55,10 @@ export class XYFileUploadService {
|
|
|
55
55
|
throw new Error(`Prepare failed: HTTP ${prepareResp.status}`);
|
|
56
56
|
}
|
|
57
57
|
const prepareData = await prepareResp.json();
|
|
58
|
-
console.log(`[XY File Upload] Prepare response:`, JSON.stringify(prepareData, null, 2));
|
|
59
58
|
if (prepareData.code !== "0") {
|
|
60
59
|
throw new Error(`Prepare failed: ${prepareData.desc}`);
|
|
61
60
|
}
|
|
62
61
|
const { objectId, draftId, uploadInfos } = prepareData;
|
|
63
|
-
console.log(`[XY File Upload] Prepare complete: objectId=${objectId}, draftId=${draftId}`);
|
|
64
62
|
// Phase 2: Upload
|
|
65
63
|
console.log(`[XY File Upload] Phase 2: Upload file data`);
|
|
66
64
|
const uploadInfo = uploadInfos[0]; // Single-part upload
|
|
@@ -69,11 +67,8 @@ export class XYFileUploadService {
|
|
|
69
67
|
headers: uploadInfo.headers,
|
|
70
68
|
body: fileBuffer,
|
|
71
69
|
});
|
|
72
|
-
console.log(`[XY File Upload] Upload response status: ${uploadResp.status}, url: ${uploadInfo.url}`);
|
|
73
|
-
console.log(`[XY File Upload] Upload response headers:`, JSON.stringify(Object.fromEntries(uploadResp.headers.entries()), null, 2));
|
|
74
70
|
if (!uploadResp.ok) {
|
|
75
71
|
const uploadErrorText = await uploadResp.text();
|
|
76
|
-
console.log(`[XY File Upload] Upload error response:`, uploadErrorText);
|
|
77
72
|
throw new Error(`Upload failed: HTTP ${uploadResp.status}`);
|
|
78
73
|
}
|
|
79
74
|
console.log(`[XY File Upload] Upload complete`);
|
|
@@ -96,7 +91,6 @@ export class XYFileUploadService {
|
|
|
96
91
|
throw new Error(`Complete failed: HTTP ${completeResp.status}`);
|
|
97
92
|
}
|
|
98
93
|
const completeData = await completeResp.json();
|
|
99
|
-
console.log(`[XY File Upload] Complete response:`, JSON.stringify(completeData, null, 2));
|
|
100
94
|
console.log(`[XY File Upload] File upload successful: ${fileName} → objectId=${objectId}`);
|
|
101
95
|
return objectId;
|
|
102
96
|
}
|
|
@@ -105,6 +99,94 @@ export class XYFileUploadService {
|
|
|
105
99
|
return "";
|
|
106
100
|
}
|
|
107
101
|
}
|
|
102
|
+
/**
|
|
103
|
+
* Upload a file and return its publicly accessible URL.
|
|
104
|
+
* Uses completeAndQuery endpoint to get the file URL directly.
|
|
105
|
+
*/
|
|
106
|
+
async uploadFileAndGetUrl(filePath, objectType = "TEMPORARY_MATERIAL_DOC") {
|
|
107
|
+
try {
|
|
108
|
+
// Read file
|
|
109
|
+
const fileBuffer = await fs.readFile(filePath);
|
|
110
|
+
const fileName = path.basename(filePath);
|
|
111
|
+
const fileSha256 = calculateSHA256(fileBuffer);
|
|
112
|
+
const fileSize = fileBuffer.length;
|
|
113
|
+
// Phase 1: Prepare
|
|
114
|
+
console.log(`[XY File Upload] Phase 1: Prepare upload for ${fileName}`);
|
|
115
|
+
const prepareResp = await fetch(`${this.baseUrl}/osms/v1/file/manager/prepare`, {
|
|
116
|
+
method: "POST",
|
|
117
|
+
headers: {
|
|
118
|
+
"Content-Type": "application/json",
|
|
119
|
+
"x-uid": this.uid,
|
|
120
|
+
"x-api-key": this.apiKey,
|
|
121
|
+
"x-request-from": "openclaw",
|
|
122
|
+
},
|
|
123
|
+
body: JSON.stringify({
|
|
124
|
+
objectType,
|
|
125
|
+
fileName,
|
|
126
|
+
fileSha256,
|
|
127
|
+
fileSize,
|
|
128
|
+
fileOwnerInfo: {
|
|
129
|
+
uid: this.uid,
|
|
130
|
+
teamId: this.uid,
|
|
131
|
+
},
|
|
132
|
+
useEdge: false,
|
|
133
|
+
}),
|
|
134
|
+
});
|
|
135
|
+
if (!prepareResp.ok) {
|
|
136
|
+
throw new Error(`Prepare failed: HTTP ${prepareResp.status}`);
|
|
137
|
+
}
|
|
138
|
+
const prepareData = await prepareResp.json();
|
|
139
|
+
if (prepareData.code !== "0") {
|
|
140
|
+
throw new Error(`Prepare failed: ${prepareData.desc}`);
|
|
141
|
+
}
|
|
142
|
+
const { objectId, draftId, uploadInfos } = prepareData;
|
|
143
|
+
console.log(`[XY File Upload] Prepare complete: objectId=${objectId}, draftId=${draftId}`);
|
|
144
|
+
// Phase 2: Upload
|
|
145
|
+
console.log(`[XY File Upload] Phase 2: Upload file data`);
|
|
146
|
+
const uploadInfo = uploadInfos[0]; // Single-part upload
|
|
147
|
+
const uploadResp = await fetch(uploadInfo.url, {
|
|
148
|
+
method: uploadInfo.method,
|
|
149
|
+
headers: uploadInfo.headers,
|
|
150
|
+
body: fileBuffer,
|
|
151
|
+
});
|
|
152
|
+
console.log(`[XY File Upload] Upload response status: ${uploadResp.status}`);
|
|
153
|
+
if (!uploadResp.ok) {
|
|
154
|
+
const uploadErrorText = await uploadResp.text();
|
|
155
|
+
throw new Error(`Upload failed: HTTP ${uploadResp.status}`);
|
|
156
|
+
}
|
|
157
|
+
console.log(`[XY File Upload] Upload complete`);
|
|
158
|
+
// Phase 3: CompleteAndQuery - get file URL
|
|
159
|
+
console.log(`[XY File Upload] Phase 3: CompleteAndQuery to get file URL`);
|
|
160
|
+
const completeResp = await fetch(`${this.baseUrl}/osms/v1/file/manager/completeAndQuery`, {
|
|
161
|
+
method: "POST",
|
|
162
|
+
headers: {
|
|
163
|
+
"Content-Type": "application/json",
|
|
164
|
+
"x-uid": this.uid,
|
|
165
|
+
"x-api-key": this.apiKey,
|
|
166
|
+
"x-request-from": "openclaw",
|
|
167
|
+
},
|
|
168
|
+
body: JSON.stringify({
|
|
169
|
+
objectId,
|
|
170
|
+
draftId,
|
|
171
|
+
}),
|
|
172
|
+
});
|
|
173
|
+
if (!completeResp.ok) {
|
|
174
|
+
throw new Error(`CompleteAndQuery failed: HTTP ${completeResp.status}`);
|
|
175
|
+
}
|
|
176
|
+
const completeData = await completeResp.json();
|
|
177
|
+
// Extract file URL from response
|
|
178
|
+
const fileUrl = completeData?.fileDetailInfo?.url || "";
|
|
179
|
+
if (!fileUrl) {
|
|
180
|
+
throw new Error("No file URL returned from completeAndQuery");
|
|
181
|
+
}
|
|
182
|
+
console.log(`[XY File Upload] File upload successful`);
|
|
183
|
+
return fileUrl;
|
|
184
|
+
}
|
|
185
|
+
catch (error) {
|
|
186
|
+
console.error(`[XY File Upload] File upload with URL retrieval failed for ${filePath}:`, error);
|
|
187
|
+
throw error;
|
|
188
|
+
}
|
|
189
|
+
}
|
|
108
190
|
/**
|
|
109
191
|
* Upload multiple files and return their file IDs.
|
|
110
192
|
*/
|
package/dist/src/formatter.d.ts
CHANGED
|
@@ -92,3 +92,17 @@ export interface SendTasksCancelResponseParams {
|
|
|
92
92
|
* Send a tasks/cancel response.
|
|
93
93
|
*/
|
|
94
94
|
export declare function sendTasksCancelResponse(params: SendTasksCancelResponseParams): Promise<void>;
|
|
95
|
+
/**
|
|
96
|
+
* Parameters for sending a Trigger response.
|
|
97
|
+
*/
|
|
98
|
+
export interface SendTriggerResponseParams {
|
|
99
|
+
config: XYChannelConfig;
|
|
100
|
+
sessionId: string;
|
|
101
|
+
taskId: string;
|
|
102
|
+
messageId: string;
|
|
103
|
+
content: string;
|
|
104
|
+
}
|
|
105
|
+
/**
|
|
106
|
+
* Send a Trigger response with pushData content.
|
|
107
|
+
*/
|
|
108
|
+
export declare function sendTriggerResponse(params: SendTriggerResponseParams): Promise<void>;
|
package/dist/src/formatter.js
CHANGED
|
@@ -52,18 +52,11 @@ export async function sendA2AResponse(params) {
|
|
|
52
52
|
msgDetail: JSON.stringify(jsonRpcResponse),
|
|
53
53
|
};
|
|
54
54
|
// 📋 Log complete response body
|
|
55
|
-
log(`[A2A_RESPONSE] 📤 Sending A2A artifact-update response
|
|
56
|
-
log(`[A2A_RESPONSE] - sessionId: ${sessionId}`);
|
|
57
|
-
log(`[A2A_RESPONSE] - taskId: ${taskId}`);
|
|
58
|
-
log(`[A2A_RESPONSE] - messageId: ${messageId}`);
|
|
55
|
+
log(`[A2A_RESPONSE] 📤 Sending A2A artifact-update response: taskId: ${taskId}`);
|
|
59
56
|
log(`[A2A_RESPONSE] - append: ${append}`);
|
|
60
57
|
log(`[A2A_RESPONSE] - final: ${final}`);
|
|
61
|
-
log(`[A2A_RESPONSE] - text
|
|
58
|
+
log(`[A2A_RESPONSE] - text: ${text.length <= 10 ? text : text.slice(0, 5) + '***' + text.slice(-5)}`);
|
|
62
59
|
log(`[A2A_RESPONSE] - files count: ${files?.length ?? 0}`);
|
|
63
|
-
log(`[A2A_RESPONSE] 📦 Complete outbound message:`);
|
|
64
|
-
log(JSON.stringify(outboundMessage, null, 2));
|
|
65
|
-
log(`[A2A_RESPONSE] 📦 JSON-RPC response body:`);
|
|
66
|
-
log(JSON.stringify(jsonRpcResponse, null, 2));
|
|
67
60
|
await wsManager.sendMessage(sessionId, outboundMessage);
|
|
68
61
|
log(`[A2A_RESPONSE] ✅ Message sent successfully`);
|
|
69
62
|
}
|
|
@@ -106,9 +99,7 @@ export async function sendReasoningTextUpdate(params) {
|
|
|
106
99
|
taskId,
|
|
107
100
|
msgDetail: JSON.stringify(jsonRpcResponse),
|
|
108
101
|
};
|
|
109
|
-
log(`[REASONING_TEXT] 📤 Sending reasoningText update: sessionId=${sessionId}, taskId=${taskId}, text.length=${text.length}`);
|
|
110
102
|
await wsManager.sendMessage(sessionId, outboundMessage);
|
|
111
|
-
log(`[REASONING_TEXT] ✅ Sent successfully`);
|
|
112
103
|
}
|
|
113
104
|
/**
|
|
114
105
|
* Send an A2A task status update.
|
|
@@ -154,17 +145,9 @@ export async function sendStatusUpdate(params) {
|
|
|
154
145
|
};
|
|
155
146
|
// 📋 Log complete response body
|
|
156
147
|
log(`[A2A_STATUS] 📤 Sending A2A status-update:`);
|
|
157
|
-
log(`[A2A_STATUS] - sessionId: ${sessionId}`);
|
|
158
148
|
log(`[A2A_STATUS] - taskId: ${taskId}`);
|
|
159
|
-
log(`[A2A_STATUS] - messageId: ${messageId}`);
|
|
160
|
-
log(`[A2A_STATUS] - state: ${state}`);
|
|
161
149
|
log(`[A2A_STATUS] - text: "${text}"`);
|
|
162
|
-
log(`[A2A_STATUS] 📦 Complete outbound message:`);
|
|
163
|
-
log(JSON.stringify(outboundMessage, null, 2));
|
|
164
|
-
log(`[A2A_STATUS] 📦 JSON-RPC response body:`);
|
|
165
|
-
log(JSON.stringify(jsonRpcResponse, null, 2));
|
|
166
150
|
await wsManager.sendMessage(sessionId, outboundMessage);
|
|
167
|
-
log(`[A2A_STATUS] ✅ Status update sent successfully`);
|
|
168
151
|
}
|
|
169
152
|
/**
|
|
170
153
|
* Send a command as an artifact update (final=false).
|
|
@@ -210,15 +193,7 @@ export async function sendCommand(params) {
|
|
|
210
193
|
msgDetail: JSON.stringify(jsonRpcResponse),
|
|
211
194
|
};
|
|
212
195
|
// 📋 Log complete response body
|
|
213
|
-
log(`[A2A_COMMAND] 📤 Sending A2A command
|
|
214
|
-
log(`[A2A_COMMAND] - sessionId: ${sessionId}`);
|
|
215
|
-
log(`[A2A_COMMAND] - taskId: ${taskId}`);
|
|
216
|
-
log(`[A2A_COMMAND] - messageId: ${messageId}`);
|
|
217
|
-
log(`[A2A_COMMAND] - command: ${command.header.namespace}::${command.header.name}`);
|
|
218
|
-
log(`[A2A_COMMAND] 📦 Complete outbound message:`);
|
|
219
|
-
log(JSON.stringify(outboundMessage, null, 2));
|
|
220
|
-
log(`[A2A_COMMAND] 📦 JSON-RPC response body:`);
|
|
221
|
-
log(JSON.stringify(jsonRpcResponse, null, 2));
|
|
196
|
+
log(`[A2A_COMMAND] 📤 Sending A2A command: taskId: ${taskId}`);
|
|
222
197
|
await wsManager.sendMessage(sessionId, outboundMessage);
|
|
223
198
|
log(`[A2A_COMMAND] ✅ Command sent successfully`);
|
|
224
199
|
}
|
|
@@ -293,3 +268,49 @@ export async function sendTasksCancelResponse(params) {
|
|
|
293
268
|
await wsManager.sendMessage(sessionId, outboundMessage);
|
|
294
269
|
log(`Sent tasks/cancel response: sessionId=${sessionId}, taskId=${taskId}`);
|
|
295
270
|
}
|
|
271
|
+
/**
|
|
272
|
+
* Send a Trigger response with pushData content.
|
|
273
|
+
*/
|
|
274
|
+
export async function sendTriggerResponse(params) {
|
|
275
|
+
const { config, sessionId, taskId, messageId, content } = params;
|
|
276
|
+
const runtime = getXYRuntime();
|
|
277
|
+
const log = runtime?.log ?? console.log;
|
|
278
|
+
const error = runtime?.error ?? console.error;
|
|
279
|
+
// Build JSON-RPC response for Trigger
|
|
280
|
+
const jsonRpcResponse = {
|
|
281
|
+
jsonrpc: "2.0",
|
|
282
|
+
id: messageId,
|
|
283
|
+
result: {
|
|
284
|
+
taskId: taskId,
|
|
285
|
+
kind: "artifact-update",
|
|
286
|
+
append: false,
|
|
287
|
+
lastChunk: true,
|
|
288
|
+
final: true,
|
|
289
|
+
artifact: {
|
|
290
|
+
artifactId: uuidv4(),
|
|
291
|
+
parts: [
|
|
292
|
+
{
|
|
293
|
+
kind: "text",
|
|
294
|
+
text: content,
|
|
295
|
+
},
|
|
296
|
+
],
|
|
297
|
+
},
|
|
298
|
+
},
|
|
299
|
+
error: {
|
|
300
|
+
code: 0,
|
|
301
|
+
message: "",
|
|
302
|
+
},
|
|
303
|
+
};
|
|
304
|
+
// Send via WebSocket
|
|
305
|
+
const wsManager = getXYWebSocketManager(config);
|
|
306
|
+
const outboundMessage = {
|
|
307
|
+
msgType: "agent_response",
|
|
308
|
+
agentId: config.agentId,
|
|
309
|
+
sessionId,
|
|
310
|
+
taskId,
|
|
311
|
+
msgDetail: JSON.stringify(jsonRpcResponse),
|
|
312
|
+
};
|
|
313
|
+
log(`[TRIGGER_RESPONSE] Sending Trigger response: sessionId=${sessionId}, taskId=${taskId}`);
|
|
314
|
+
await wsManager.sendMessage(sessionId, outboundMessage);
|
|
315
|
+
log(`[TRIGGER_RESPONSE] Trigger response sent successfully`);
|
|
316
|
+
}
|
package/dist/src/heartbeat.js
CHANGED
|
@@ -72,18 +72,14 @@ export class HeartbeatManager {
|
|
|
72
72
|
}
|
|
73
73
|
try {
|
|
74
74
|
// Send application-level heartbeat message
|
|
75
|
-
console.log(`[WS-${this.serverName}-SEND] Sending heartbeat frame:`, this.config.message);
|
|
76
75
|
this.ws.send(this.config.message);
|
|
77
|
-
console.log(`[WS-${this.serverName}-SEND] Heartbeat message sent, size: ${this.config.message.length} bytes`);
|
|
78
76
|
// Send protocol-level ping
|
|
79
77
|
this.ws.ping();
|
|
80
|
-
console.log(`[WS-${this.serverName}-SEND] Protocol-level ping sent`);
|
|
81
78
|
// Setup timeout timer
|
|
82
79
|
this.timeoutTimer = setTimeout(() => {
|
|
83
80
|
this.error(`Heartbeat timeout for ${this.serverName}`);
|
|
84
81
|
this.onTimeout();
|
|
85
82
|
}, this.config.timeout);
|
|
86
|
-
this.log(`[DEBUG] Heartbeat sent for ${this.serverName}`);
|
|
87
83
|
}
|
|
88
84
|
catch (error) {
|
|
89
85
|
this.error(`Failed to send heartbeat for ${this.serverName}:`, error);
|
package/dist/src/monitor.js
CHANGED
|
@@ -3,6 +3,8 @@ import { getXYWebSocketManager, diagnoseAllManagers, cleanupOrphanConnections, r
|
|
|
3
3
|
import { handleXYMessage } from "./bot.js";
|
|
4
4
|
import { parseA2AMessage } from "./parser.js";
|
|
5
5
|
import { hasActiveTask } from "./task-manager.js";
|
|
6
|
+
import { handleTriggerEvent } from "./trigger-handler.js";
|
|
7
|
+
import { cleanupStaleTempFiles } from "./reply-dispatcher.js";
|
|
6
8
|
/**
|
|
7
9
|
* Per-session serial queue that ensures messages from the same session are processed
|
|
8
10
|
* in arrival order while allowing different sessions to run concurrently.
|
|
@@ -66,7 +68,7 @@ export async function monitorXYProvider(opts = {}) {
|
|
|
66
68
|
// Event handlers (defined early so they can be referenced in cleanup)
|
|
67
69
|
const messageHandler = (message, sessionId, serverId) => {
|
|
68
70
|
const messageKey = `${sessionId}::${message.id}`;
|
|
69
|
-
log(`[MONITOR-HANDLER] ####### messageHandler triggered:
|
|
71
|
+
log(`[MONITOR-HANDLER] ####### messageHandler triggered: sessionId=${sessionId}, messageId=${message.id} #######`);
|
|
70
72
|
// ✅ Report health: received a message
|
|
71
73
|
trackEvent?.();
|
|
72
74
|
// Check for duplicate message handling
|
|
@@ -74,17 +76,14 @@ export async function monitorXYProvider(opts = {}) {
|
|
|
74
76
|
error(`[MONITOR-HANDLER] ⚠️ WARNING: Duplicate message detected! messageKey=${messageKey}, this may cause duplicate dispatchers!`);
|
|
75
77
|
}
|
|
76
78
|
activeMessages.add(messageKey);
|
|
77
|
-
log(`[MONITOR-HANDLER] 📝 Active messages count: ${activeMessages.size}, messageKey: ${messageKey}`);
|
|
78
79
|
const task = async () => {
|
|
79
80
|
try {
|
|
80
|
-
log(`[MONITOR-HANDLER] 🚀 Starting handleXYMessage for messageKey=${messageKey}`);
|
|
81
81
|
await handleXYMessage({
|
|
82
82
|
cfg,
|
|
83
83
|
runtime,
|
|
84
84
|
message,
|
|
85
85
|
accountId, // ✅ Pass accountId ("default")
|
|
86
86
|
});
|
|
87
|
-
log(`[MONITOR-HANDLER] ✅ Completed handleXYMessage for messageKey=${messageKey}`);
|
|
88
87
|
}
|
|
89
88
|
catch (err) {
|
|
90
89
|
// ✅ Only log error, don't re-throw to prevent gateway restart
|
|
@@ -93,7 +92,6 @@ export async function monitorXYProvider(opts = {}) {
|
|
|
93
92
|
finally {
|
|
94
93
|
// Remove from active messages when done
|
|
95
94
|
activeMessages.delete(messageKey);
|
|
96
|
-
log(`[MONITOR-HANDLER] 🧹 Cleaned up messageKey=${messageKey}, remaining active: ${activeMessages.size}`);
|
|
97
95
|
}
|
|
98
96
|
};
|
|
99
97
|
// 🔑 核心改造:检测steer模式
|
|
@@ -106,7 +104,6 @@ export async function monitorXYProvider(opts = {}) {
|
|
|
106
104
|
// Steer模式且有活跃任务:不入队列,直接并发执行
|
|
107
105
|
log(`[MONITOR-HANDLER] 🔄 STEER MODE: Executing concurrently for messageKey=${messageKey}`);
|
|
108
106
|
log(`[MONITOR-HANDLER] - sessionId: ${parsed.sessionId}`);
|
|
109
|
-
log(`[MONITOR-HANDLER] - Bypassing queue to allow message insertion`);
|
|
110
107
|
void task().catch((err) => {
|
|
111
108
|
error(`XY gateway: concurrent steer task failed for ${messageKey}: ${String(err)}`);
|
|
112
109
|
activeMessages.delete(messageKey);
|
|
@@ -114,7 +111,6 @@ export async function monitorXYProvider(opts = {}) {
|
|
|
114
111
|
}
|
|
115
112
|
else {
|
|
116
113
|
// 正常模式:入队列串行执行
|
|
117
|
-
log(`[MONITOR-HANDLER] 📋 NORMAL MODE: Enqueuing for messageKey=${messageKey}`);
|
|
118
114
|
void enqueue(sessionId, task).catch((err) => {
|
|
119
115
|
error(`XY gateway: queue processing failed for session ${sessionId}: ${String(err)}`);
|
|
120
116
|
activeMessages.delete(messageKey);
|
|
@@ -150,6 +146,15 @@ export async function monitorXYProvider(opts = {}) {
|
|
|
150
146
|
const errorHandler = (err, serverId) => {
|
|
151
147
|
error(`XY gateway: ${serverId} error: ${String(err)}`);
|
|
152
148
|
};
|
|
149
|
+
const triggerEventHandler = (context) => {
|
|
150
|
+
log(`[MONITOR] 📌 Received trigger-event, dispatching to handler...`);
|
|
151
|
+
log(`[MONITOR] - sessionId: ${context.sessionId}`);
|
|
152
|
+
log(`[MONITOR] - taskId: ${context.taskId}`);
|
|
153
|
+
// 异步处理 Trigger 事件,不阻塞主流程
|
|
154
|
+
handleTriggerEvent(context, cfg, runtime, accountId).catch((err) => {
|
|
155
|
+
error(`[MONITOR] Failed to handle trigger-event:`, err);
|
|
156
|
+
});
|
|
157
|
+
};
|
|
153
158
|
const cleanup = () => {
|
|
154
159
|
log("XY gateway: cleaning up...");
|
|
155
160
|
// 🔍 Diagnose before cleanup
|
|
@@ -166,6 +171,7 @@ export async function monitorXYProvider(opts = {}) {
|
|
|
166
171
|
wsManager.off("connected", connectedHandler);
|
|
167
172
|
wsManager.off("disconnected", disconnectedHandler);
|
|
168
173
|
wsManager.off("error", errorHandler);
|
|
174
|
+
wsManager.off("trigger-event", triggerEventHandler);
|
|
169
175
|
// ✅ Disconnect the wsManager to prevent connection leaks
|
|
170
176
|
// This is safe because each gateway lifecycle should have clean connections
|
|
171
177
|
wsManager.disconnect();
|
|
@@ -195,8 +201,9 @@ export async function monitorXYProvider(opts = {}) {
|
|
|
195
201
|
wsManager.on("connected", connectedHandler);
|
|
196
202
|
wsManager.on("disconnected", disconnectedHandler);
|
|
197
203
|
wsManager.on("error", errorHandler);
|
|
198
|
-
|
|
199
|
-
|
|
204
|
+
wsManager.on("trigger-event", triggerEventHandler);
|
|
205
|
+
// Start periodic health check (every 6 hours)
|
|
206
|
+
console.log("🏥 Starting periodic health check (every 6 hours)...");
|
|
200
207
|
healthCheckInterval = setInterval(() => {
|
|
201
208
|
console.log("🏥 [HEALTH CHECK] Periodic WebSocket diagnostics...");
|
|
202
209
|
diagnoseAllManagers();
|
|
@@ -205,7 +212,9 @@ export async function monitorXYProvider(opts = {}) {
|
|
|
205
212
|
if (cleaned > 0) {
|
|
206
213
|
console.log(`🧹 [HEALTH CHECK] Auto-cleaned ${cleaned} manager(s) with orphan connections`);
|
|
207
214
|
}
|
|
208
|
-
|
|
215
|
+
// Cleanup stale temp files (older than 24 hours)
|
|
216
|
+
void cleanupStaleTempFiles();
|
|
217
|
+
}, 6 * 60 * 60 * 1000); // 6 hours
|
|
209
218
|
// Connect to WebSocket servers
|
|
210
219
|
wsManager.connect()
|
|
211
220
|
.then(() => {
|
package/dist/src/onboarding.d.ts
CHANGED
|
@@ -1,6 +1,5 @@
|
|
|
1
|
-
import type { ChannelOnboardingAdapter } from "openclaw/plugin-sdk";
|
|
2
1
|
/**
|
|
3
|
-
* XY Channel Onboarding Adapter
|
|
4
|
-
*
|
|
2
|
+
* XY Channel Onboarding Adapter (DISABLED - not currently used)
|
|
3
|
+
* NOTE: This is kept for future reference. Xiaoyi uses simple single-account config.
|
|
5
4
|
*/
|
|
6
|
-
export declare const xyOnboardingAdapter:
|
|
5
|
+
export declare const xyOnboardingAdapter: any;
|
package/dist/src/onboarding.js
CHANGED
|
@@ -152,8 +152,8 @@ async function configure({ cfg, prompter, }) {
|
|
|
152
152
|
};
|
|
153
153
|
}
|
|
154
154
|
/**
|
|
155
|
-
* XY Channel Onboarding Adapter
|
|
156
|
-
*
|
|
155
|
+
* XY Channel Onboarding Adapter (DISABLED - not currently used)
|
|
156
|
+
* NOTE: This is kept for future reference. Xiaoyi uses simple single-account config.
|
|
157
157
|
*/
|
|
158
158
|
export const xyOnboardingAdapter = {
|
|
159
159
|
channel,
|
package/dist/src/outbound.d.ts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
|
-
|
|
1
|
+
type ChannelOutboundAdapter = any;
|
|
2
2
|
/**
|
|
3
3
|
* Outbound adapter for sending messages from OpenClaw to XY.
|
|
4
4
|
* Uses Push service for direct message delivery.
|
|
5
5
|
*/
|
|
6
6
|
export declare const xyOutbound: ChannelOutboundAdapter;
|
|
7
|
+
export {};
|