@ynhcj/xiaoyi-channel 0.0.154-beta → 0.0.156-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/dist/src/cron-command.d.ts +4 -5
- package/dist/src/cron-command.js +19 -34
- package/dist/src/push.d.ts +11 -1
- package/dist/src/push.js +93 -2
- package/package.json +1 -2
|
@@ -7,10 +7,9 @@ export interface SendCommandViaPushParams {
|
|
|
7
7
|
* Send a tool command through the push channel (for cron-triggered tool calls).
|
|
8
8
|
*
|
|
9
9
|
* Flow:
|
|
10
|
-
* 1.
|
|
11
|
-
* 2.
|
|
12
|
-
* 3. Device
|
|
13
|
-
* 4.
|
|
14
|
-
* 5. The calling tool listens on the WebSocket manager as usual
|
|
10
|
+
* 1. Push notification is sent with command embedded in data.directives
|
|
11
|
+
* 2. Device receives push → extracts directives → executes command
|
|
12
|
+
* 3. Device returns result via WebSocket (data-event / gui-agent-response / …)
|
|
13
|
+
* 4. The calling tool listens on the WebSocket manager as usual
|
|
15
14
|
*/
|
|
16
15
|
export declare function sendCommandViaPush(params: SendCommandViaPushParams): Promise<void>;
|
package/dist/src/cron-command.js
CHANGED
|
@@ -5,60 +5,45 @@
|
|
|
5
5
|
// independently of any session. The device processes the command and returns
|
|
6
6
|
// results through the normal WebSocket connection, so response listening
|
|
7
7
|
// works the same as for regular tool calls.
|
|
8
|
+
import { randomUUID } from "crypto";
|
|
8
9
|
import { XYPushService } from "./push.js";
|
|
9
|
-
import { savePushData } from "./utils/pushdata-manager.js";
|
|
10
10
|
import { getAllPushIds } from "./utils/pushid-manager.js";
|
|
11
11
|
import { logger } from "./utils/logger.js";
|
|
12
12
|
/**
|
|
13
13
|
* Send a tool command through the push channel (for cron-triggered tool calls).
|
|
14
14
|
*
|
|
15
15
|
* Flow:
|
|
16
|
-
* 1.
|
|
17
|
-
* 2.
|
|
18
|
-
* 3. Device
|
|
19
|
-
* 4.
|
|
20
|
-
* 5. The calling tool listens on the WebSocket manager as usual
|
|
16
|
+
* 1. Push notification is sent with command embedded in data.directives
|
|
17
|
+
* 2. Device receives push → extracts directives → executes command
|
|
18
|
+
* 3. Device returns result via WebSocket (data-event / gui-agent-response / …)
|
|
19
|
+
* 4. The calling tool listens on the WebSocket manager as usual
|
|
21
20
|
*/
|
|
22
21
|
export async function sendCommandViaPush(params) {
|
|
23
22
|
const { config, command } = params;
|
|
24
|
-
const commandJson = JSON.stringify(command);
|
|
25
23
|
const intentName = command.payload?.executeParam?.intentName ??
|
|
26
24
|
command.header?.name ??
|
|
27
25
|
"Command";
|
|
28
26
|
logger.log(`[CRON-CMD] Sending command via push, intent=${intentName}`);
|
|
29
|
-
// 1.
|
|
30
|
-
let
|
|
27
|
+
// 1. Load push IDs, use first one
|
|
28
|
+
let pushId = config.pushId;
|
|
31
29
|
try {
|
|
32
|
-
|
|
33
|
-
|
|
30
|
+
const pushIdList = await getAllPushIds();
|
|
31
|
+
if (pushIdList.length > 0) {
|
|
32
|
+
pushId = pushIdList[0];
|
|
33
|
+
}
|
|
34
34
|
}
|
|
35
35
|
catch (error) {
|
|
36
|
-
logger.error("[CRON-CMD] Failed to
|
|
36
|
+
logger.error("[CRON-CMD] Failed to load pushIds:", error);
|
|
37
37
|
}
|
|
38
|
-
// 2.
|
|
39
|
-
|
|
38
|
+
// 2. Build and send push notification with command in directives
|
|
39
|
+
const pushService = new XYPushService(config);
|
|
40
|
+
const sessionId = randomUUID();
|
|
40
41
|
try {
|
|
41
|
-
|
|
42
|
+
await pushService.sendPushWithDirectives(pushId, sessionId, [command]);
|
|
43
|
+
logger.log(`[CRON-CMD] Push sent successfully, intent=${intentName}`);
|
|
42
44
|
}
|
|
43
45
|
catch (error) {
|
|
44
|
-
logger.error(
|
|
45
|
-
|
|
46
|
-
if (pushIdList.length === 0) {
|
|
47
|
-
pushIdList = [config.pushId];
|
|
48
|
-
}
|
|
49
|
-
// 3. Broadcast push notification
|
|
50
|
-
const pushService = new XYPushService(config);
|
|
51
|
-
const title = `定时任务: ${intentName}`;
|
|
52
|
-
const pushText = commandJson.length > 1000 ? commandJson.slice(0, 1000) : commandJson;
|
|
53
|
-
let successCount = 0;
|
|
54
|
-
for (const pushId of pushIdList) {
|
|
55
|
-
try {
|
|
56
|
-
await pushService.sendPush(pushText, title, undefined, config.defaultSessionId || "", pushDataId, pushId);
|
|
57
|
-
successCount++;
|
|
58
|
-
}
|
|
59
|
-
catch (error) {
|
|
60
|
-
logger.error(`[CRON-CMD] Failed to send push to pushId=${pushId.substring(0, 20)}`, error);
|
|
61
|
-
}
|
|
46
|
+
logger.error(`[CRON-CMD] Failed to send push`, error);
|
|
47
|
+
throw error;
|
|
62
48
|
}
|
|
63
|
-
logger.log(`[CRON-CMD] Push sent to ${successCount}/${pushIdList.length} pushId(s), intent=${intentName}`);
|
|
64
49
|
}
|
package/dist/src/push.d.ts
CHANGED
|
@@ -5,9 +5,14 @@ import type { XYChannelConfig } from "./types.js";
|
|
|
5
5
|
*/
|
|
6
6
|
export declare class XYPushService {
|
|
7
7
|
private config;
|
|
8
|
-
private readonly
|
|
8
|
+
private readonly PROD_PUSH_URL;
|
|
9
|
+
private readonly TEST_PUSH_URL;
|
|
9
10
|
private readonly REQUEST_FROM;
|
|
10
11
|
constructor(config: XYChannelConfig);
|
|
12
|
+
/**
|
|
13
|
+
* Resolve push URL: config.pushUrl > inferred from fileUploadUrl > production default.
|
|
14
|
+
*/
|
|
15
|
+
private resolvePushUrl;
|
|
11
16
|
/**
|
|
12
17
|
* Generate a random trace ID for request tracking.
|
|
13
18
|
*/
|
|
@@ -23,6 +28,11 @@ export declare class XYPushService {
|
|
|
23
28
|
* @param pushId - Push ID to use (required)
|
|
24
29
|
*/
|
|
25
30
|
sendPush(content: string, title: string, data?: Record<string, any>, sessionId?: string, pushDataId?: string, pushId?: string): Promise<void>;
|
|
31
|
+
/**
|
|
32
|
+
* Send a push message with command directives embedded directly.
|
|
33
|
+
* Used for cron-triggered commands where pushText is empty and pushType=101.
|
|
34
|
+
*/
|
|
35
|
+
sendPushWithDirectives(pushId: string, sessionId: string, directives: any[]): Promise<void>;
|
|
26
36
|
/**
|
|
27
37
|
* Send a push message with file attachments.
|
|
28
38
|
*/
|
package/dist/src/push.js
CHANGED
|
@@ -8,11 +8,24 @@ import { logger } from "./utils/logger.js";
|
|
|
8
8
|
*/
|
|
9
9
|
export class XYPushService {
|
|
10
10
|
config;
|
|
11
|
-
|
|
11
|
+
PROD_PUSH_URL = "https://hag.cloud.huawei.com/open-ability-agent/v1/agent-webhook";
|
|
12
|
+
TEST_PUSH_URL = "https://lfhagcp.hwcloudtest.cn:58447/open-ability-agent/v1/agent-webhook";
|
|
12
13
|
REQUEST_FROM = "openclaw";
|
|
13
14
|
constructor(config) {
|
|
14
15
|
this.config = config;
|
|
15
16
|
}
|
|
17
|
+
/**
|
|
18
|
+
* Resolve push URL: config.pushUrl > inferred from fileUploadUrl > production default.
|
|
19
|
+
*/
|
|
20
|
+
resolvePushUrl() {
|
|
21
|
+
if (this.config.pushUrl) {
|
|
22
|
+
return this.config.pushUrl;
|
|
23
|
+
}
|
|
24
|
+
if (this.config.fileUploadUrl?.includes("lfhagmirror")) {
|
|
25
|
+
return this.TEST_PUSH_URL;
|
|
26
|
+
}
|
|
27
|
+
return this.PROD_PUSH_URL;
|
|
28
|
+
}
|
|
16
29
|
/**
|
|
17
30
|
* Generate a random trace ID for request tracking.
|
|
18
31
|
*/
|
|
@@ -30,7 +43,7 @@ export class XYPushService {
|
|
|
30
43
|
* @param pushId - Push ID to use (required)
|
|
31
44
|
*/
|
|
32
45
|
async sendPush(content, title, data, sessionId, pushDataId, pushId) {
|
|
33
|
-
const pushUrl = this.
|
|
46
|
+
const pushUrl = this.resolvePushUrl();
|
|
34
47
|
const traceId = this.generateTraceId();
|
|
35
48
|
// Use provided pushId or fall back to config pushId
|
|
36
49
|
const actualPushId = pushId || this.config.pushId;
|
|
@@ -114,6 +127,84 @@ export class XYPushService {
|
|
|
114
127
|
throw error;
|
|
115
128
|
}
|
|
116
129
|
}
|
|
130
|
+
/**
|
|
131
|
+
* Send a push message with command directives embedded directly.
|
|
132
|
+
* Used for cron-triggered commands where pushText is empty and pushType=101.
|
|
133
|
+
*/
|
|
134
|
+
async sendPushWithDirectives(pushId, sessionId, directives) {
|
|
135
|
+
const pushUrl = this.resolvePushUrl();
|
|
136
|
+
const traceId = this.generateTraceId();
|
|
137
|
+
logger.log(`[PUSH] Preparing to send push with directives, pushId: ${pushId.substring(0, 20)}...`);
|
|
138
|
+
const requestBody = {
|
|
139
|
+
jsonrpc: "2.0",
|
|
140
|
+
id: randomUUID(),
|
|
141
|
+
result: {
|
|
142
|
+
id: randomUUID(),
|
|
143
|
+
apiId: this.config.apiId,
|
|
144
|
+
pushId,
|
|
145
|
+
pushText: "",
|
|
146
|
+
pushType: 101,
|
|
147
|
+
kind: "task",
|
|
148
|
+
sessionId,
|
|
149
|
+
artifacts: [
|
|
150
|
+
{
|
|
151
|
+
artifactId: randomUUID(),
|
|
152
|
+
parts: [
|
|
153
|
+
{
|
|
154
|
+
kind: "data",
|
|
155
|
+
data: { directives },
|
|
156
|
+
},
|
|
157
|
+
],
|
|
158
|
+
},
|
|
159
|
+
],
|
|
160
|
+
},
|
|
161
|
+
};
|
|
162
|
+
try {
|
|
163
|
+
const response = await fetch(pushUrl, {
|
|
164
|
+
method: "POST",
|
|
165
|
+
headers: {
|
|
166
|
+
"Content-Type": "application/json",
|
|
167
|
+
"Accept": "application/json",
|
|
168
|
+
"x-hag-trace-id": traceId,
|
|
169
|
+
"x-uid": this.config.uid,
|
|
170
|
+
"x-api-key": this.config.apiKey,
|
|
171
|
+
"x-request-from": this.REQUEST_FROM,
|
|
172
|
+
},
|
|
173
|
+
body: JSON.stringify(requestBody),
|
|
174
|
+
});
|
|
175
|
+
logger.log(`[PUSH] Response received, HTTP Status: ${response.status} ${response.statusText}`);
|
|
176
|
+
if (!response.ok) {
|
|
177
|
+
const errorText = await response.text();
|
|
178
|
+
logger.error(`[PUSH] Push request failed, HTTP Status: ${response.status}`);
|
|
179
|
+
throw new Error(`Push failed: HTTP ${response.status} - ${errorText}`);
|
|
180
|
+
}
|
|
181
|
+
let result;
|
|
182
|
+
try {
|
|
183
|
+
const responseText = await response.text();
|
|
184
|
+
if (!responseText || responseText.trim() === '') {
|
|
185
|
+
logger.error(`[PUSH] Received empty response body`);
|
|
186
|
+
result = {};
|
|
187
|
+
}
|
|
188
|
+
else {
|
|
189
|
+
result = JSON.parse(responseText);
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
catch (parseError) {
|
|
193
|
+
logger.error(`[PUSH] Failed to parse JSON response: ${parseError instanceof Error ? parseError.message : String(parseError)}`);
|
|
194
|
+
throw new Error(`Invalid JSON response from push service: ${parseError instanceof Error ? parseError.message : String(parseError)}`);
|
|
195
|
+
}
|
|
196
|
+
logger.log(`[PUSH] Push message sent successfully, Trace ID: ${traceId}`);
|
|
197
|
+
}
|
|
198
|
+
catch (error) {
|
|
199
|
+
if (error instanceof Error) {
|
|
200
|
+
logger.error(`[PUSH] Failed to send push message: ${error.name} - ${error.message}`);
|
|
201
|
+
}
|
|
202
|
+
else {
|
|
203
|
+
logger.error(`[PUSH] Failed to send push message:`, error);
|
|
204
|
+
}
|
|
205
|
+
throw error;
|
|
206
|
+
}
|
|
207
|
+
}
|
|
117
208
|
/**
|
|
118
209
|
* Send a push message with file attachments.
|
|
119
210
|
*/
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ynhcj/xiaoyi-channel",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.156-beta",
|
|
4
4
|
"description": "OpenClaw Xiaoyi Channel plugin - Xiaoyi A2A protocol integration",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.js",
|
|
@@ -66,7 +66,6 @@
|
|
|
66
66
|
"@types/node": "^20.8.0",
|
|
67
67
|
"@types/uuid": "^9.0.5",
|
|
68
68
|
"@types/ws": "^8.5.8",
|
|
69
|
-
"openclaw": "^2026.5.7",
|
|
70
69
|
"typescript": "^5.9.2"
|
|
71
70
|
}
|
|
72
71
|
}
|