@ynhcj/xiaoyi-channel 0.0.115-beta → 0.0.115-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 +4 -5
- package/dist/index.js +72 -82
- package/dist/provider-discovery.d.ts +2 -0
- package/dist/provider-discovery.js +4 -0
- package/dist/src/bot.d.ts +2 -0
- package/dist/src/bot.js +80 -72
- package/dist/src/channel.js +2 -17
- package/dist/src/client.d.ts +1 -5
- package/dist/src/client.js +32 -37
- package/dist/src/cspl/call-api.js +6 -5
- package/dist/src/file-download.js +4 -3
- package/dist/src/file-upload.js +19 -18
- package/dist/src/formatter.js +32 -44
- package/dist/src/heartbeat.js +4 -3
- package/dist/src/login-token-handler.js +7 -8
- package/dist/src/message-queue.js +2 -1
- package/dist/src/monitor.js +62 -41
- package/dist/src/outbound.js +22 -18
- package/dist/src/provider.d.ts +1 -1
- package/dist/src/provider.js +124 -34
- package/dist/src/push.js +16 -15
- package/dist/src/reply-dispatcher.js +47 -48
- package/dist/src/runtime.d.ts +3 -11
- package/dist/src/runtime.js +6 -18
- package/dist/src/self-evolution-handler.js +11 -14
- package/dist/src/self-evolution-tool-result-nudge.d.ts +3 -0
- package/dist/src/self-evolution-tool-result-nudge.js +96 -0
- package/dist/src/skill-retriever/hooks.js +7 -15
- package/dist/src/skill-retriever/tool-search.js +13 -9
- package/dist/src/steer-injector.js +1 -1
- package/dist/src/task-manager.js +6 -1
- package/dist/src/tools/calendar-tool.d.ts +2 -1
- package/dist/src/tools/calendar-tool.js +112 -116
- package/dist/src/tools/call-device-tool.d.ts +2 -1
- package/dist/src/tools/call-device-tool.js +126 -103
- package/dist/src/tools/call-phone-tool.d.ts +2 -1
- package/dist/src/tools/call-phone-tool.js +109 -113
- package/dist/src/tools/create-alarm-tool.d.ts +2 -1
- package/dist/src/tools/create-alarm-tool.js +227 -231
- package/dist/src/tools/create-all-tools.d.ts +16 -0
- package/dist/src/tools/create-all-tools.js +50 -0
- package/dist/src/tools/delete-alarm-tool.d.ts +2 -1
- package/dist/src/tools/delete-alarm-tool.js +131 -135
- package/dist/src/tools/get-alarm-tool-schema.d.ts +2 -1
- package/dist/src/tools/get-alarm-tool-schema.js +16 -10
- package/dist/src/tools/get-calendar-tool-schema.d.ts +2 -1
- package/dist/src/tools/get-calendar-tool-schema.js +12 -8
- package/dist/src/tools/get-collection-tool-schema.d.ts +2 -1
- package/dist/src/tools/get-collection-tool-schema.js +11 -9
- package/dist/src/tools/get-contact-tool-schema.d.ts +2 -1
- package/dist/src/tools/get-contact-tool-schema.js +16 -10
- package/dist/src/tools/get-device-file-tool-schema.d.ts +2 -1
- package/dist/src/tools/get-device-file-tool-schema.js +13 -9
- package/dist/src/tools/get-email-tool-schema.d.ts +2 -1
- package/dist/src/tools/get-email-tool-schema.js +11 -8
- package/dist/src/tools/get-note-tool-schema.d.ts +2 -1
- package/dist/src/tools/get-note-tool-schema.js +14 -9
- package/dist/src/tools/get-photo-tool-schema.d.ts +2 -1
- package/dist/src/tools/get-photo-tool-schema.js +12 -9
- package/dist/src/tools/image-reading-tool.d.ts +3 -2
- package/dist/src/tools/image-reading-tool.js +82 -162
- package/dist/src/tools/location-tool.d.ts +2 -1
- package/dist/src/tools/location-tool.js +87 -91
- package/dist/src/tools/login-token-tool.d.ts +2 -1
- package/dist/src/tools/login-token-tool.js +113 -116
- package/dist/src/tools/modify-alarm-tool.d.ts +2 -1
- package/dist/src/tools/modify-alarm-tool.js +232 -236
- package/dist/src/tools/modify-note-tool.d.ts +2 -1
- package/dist/src/tools/modify-note-tool.js +104 -108
- package/dist/src/tools/note-tool.d.ts +2 -1
- package/dist/src/tools/note-tool.js +103 -107
- package/dist/src/tools/query-app-message-tool.d.ts +2 -1
- package/dist/src/tools/query-app-message-tool.js +108 -111
- package/dist/src/tools/query-memory-data-tool.d.ts +2 -1
- package/dist/src/tools/query-memory-data-tool.js +109 -112
- package/dist/src/tools/query-todo-task-tool.d.ts +2 -1
- package/dist/src/tools/query-todo-task-tool.js +103 -106
- package/dist/src/tools/save-file-to-phone-tool.d.ts +2 -1
- package/dist/src/tools/save-file-to-phone-tool.js +127 -131
- package/dist/src/tools/save-media-to-gallery-tool.d.ts +2 -1
- package/dist/src/tools/save-media-to-gallery-tool.js +134 -138
- package/dist/src/tools/save-self-evolution-skill-tool.d.ts +2 -1
- package/dist/src/tools/save-self-evolution-skill-tool.js +194 -196
- package/dist/src/tools/search-alarm-tool.d.ts +2 -1
- package/dist/src/tools/search-alarm-tool.js +171 -175
- package/dist/src/tools/search-calendar-tool.d.ts +2 -1
- package/dist/src/tools/search-calendar-tool.js +145 -149
- package/dist/src/tools/search-contact-tool.d.ts +2 -1
- package/dist/src/tools/search-contact-tool.js +98 -102
- package/dist/src/tools/search-email-tool.d.ts +2 -1
- package/dist/src/tools/search-email-tool.js +107 -111
- package/dist/src/tools/search-file-tool.d.ts +2 -1
- package/dist/src/tools/search-file-tool.js +99 -103
- package/dist/src/tools/search-message-tool.d.ts +2 -1
- package/dist/src/tools/search-message-tool.js +100 -104
- package/dist/src/tools/search-note-tool.d.ts +2 -1
- package/dist/src/tools/search-note-tool.js +95 -99
- package/dist/src/tools/search-photo-gallery-tool.d.ts +2 -1
- package/dist/src/tools/search-photo-gallery-tool.js +34 -38
- package/dist/src/tools/send-email-tool.d.ts +2 -1
- package/dist/src/tools/send-email-tool.js +105 -108
- package/dist/src/tools/send-file-to-user-tool.d.ts +2 -1
- package/dist/src/tools/send-file-to-user-tool.js +157 -155
- package/dist/src/tools/send-message-tool.d.ts +2 -1
- package/dist/src/tools/send-message-tool.js +119 -123
- package/dist/src/tools/session-helper.d.ts +24 -0
- package/dist/src/tools/session-helper.js +45 -0
- package/dist/src/tools/session-manager.d.ts +29 -6
- package/dist/src/tools/session-manager.js +134 -19
- package/dist/src/tools/upload-file-tool.d.ts +2 -1
- package/dist/src/tools/upload-file-tool.js +78 -82
- package/dist/src/tools/upload-photo-tool.d.ts +2 -1
- package/dist/src/tools/upload-photo-tool.js +69 -73
- package/dist/src/tools/xiaoyi-add-collection-tool.d.ts +2 -1
- package/dist/src/tools/xiaoyi-add-collection-tool.js +143 -147
- package/dist/src/tools/xiaoyi-collection-tool.d.ts +2 -1
- package/dist/src/tools/xiaoyi-collection-tool.js +111 -115
- package/dist/src/tools/xiaoyi-delete-collection-tool.d.ts +2 -1
- package/dist/src/tools/xiaoyi-delete-collection-tool.js +124 -128
- package/dist/src/tools/xiaoyi-gui-tool.d.ts +2 -1
- package/dist/src/tools/xiaoyi-gui-tool.js +87 -88
- package/dist/src/trigger-handler.js +8 -9
- package/dist/src/utils/logger.js +105 -19
- package/dist/src/utils/self-evolution-manager.d.ts +5 -0
- package/dist/src/utils/self-evolution-manager.js +45 -23
- package/dist/src/utils/throw.d.ts +5 -0
- package/dist/src/utils/throw.js +10 -0
- package/dist/src/websocket.js +35 -31
- package/dist/src/xy-session-store.d.ts +79 -0
- package/dist/src/xy-session-store.js +153 -0
- package/openclaw.plugin.json +22 -0
- package/package.json +7 -6
package/dist/src/client.js
CHANGED
|
@@ -1,36 +1,33 @@
|
|
|
1
1
|
// WebSocket client cache management
|
|
2
2
|
// Follows feishu/client.ts pattern for caching client instances
|
|
3
3
|
import { XYWebSocketManager } from "./websocket.js";
|
|
4
|
-
|
|
5
|
-
let runtime;
|
|
6
|
-
/**
|
|
7
|
-
* Set the runtime for logging in client module.
|
|
8
|
-
*/
|
|
9
|
-
export function setClientRuntime(rt) {
|
|
10
|
-
runtime = rt;
|
|
11
|
-
}
|
|
4
|
+
import { logger } from "./utils/logger.js";
|
|
12
5
|
/**
|
|
13
6
|
* Global cache for WebSocket managers.
|
|
14
7
|
* Key format: `${apiKey}-${agentId}`
|
|
8
|
+
* Uses globalThis to ensure a single cache across all module copies
|
|
9
|
+
* (same fix as session-manager.ts for openclaw multi-instance loading).
|
|
15
10
|
*/
|
|
16
|
-
const
|
|
11
|
+
const _g = globalThis;
|
|
12
|
+
if (!_g.__xyWsManagerCache) {
|
|
13
|
+
_g.__xyWsManagerCache = new Map();
|
|
14
|
+
}
|
|
15
|
+
const wsManagerCache = _g.__xyWsManagerCache;
|
|
17
16
|
/**
|
|
18
17
|
* Get or create a WebSocket manager for the given configuration.
|
|
19
18
|
* Reuses existing managers if config matches.
|
|
20
19
|
*/
|
|
21
|
-
export function getXYWebSocketManager(config) {
|
|
20
|
+
export function getXYWebSocketManager(config, runtime) {
|
|
22
21
|
const cacheKey = `${config.apiKey}-${config.agentId}`;
|
|
23
22
|
let cached = wsManagerCache.get(cacheKey);
|
|
24
23
|
if (cached && cached.isConfigMatch(config)) {
|
|
25
|
-
const log = runtime?.log ?? console.log;
|
|
26
24
|
return cached;
|
|
27
25
|
}
|
|
28
26
|
// Create new manager
|
|
29
|
-
|
|
30
|
-
log(`[WS-MANAGER-CACHE] 🆕 Creating new WebSocket manager: ${cacheKey}, total managers before: ${wsManagerCache.size}`);
|
|
27
|
+
logger.log(`[WS-MANAGER-CACHE] 🆕 Creating new WebSocket manager: ${cacheKey}, total managers before: ${wsManagerCache.size}`);
|
|
31
28
|
cached = new XYWebSocketManager(config, runtime);
|
|
32
29
|
wsManagerCache.set(cacheKey, cached);
|
|
33
|
-
log(`[WS-MANAGER-CACHE] 📊 Total managers after creation: ${wsManagerCache.size}`);
|
|
30
|
+
logger.log(`[WS-MANAGER-CACHE] 📊 Total managers after creation: ${wsManagerCache.size}`);
|
|
34
31
|
return cached;
|
|
35
32
|
}
|
|
36
33
|
/**
|
|
@@ -41,21 +38,20 @@ export function removeXYWebSocketManager(config) {
|
|
|
41
38
|
const cacheKey = `${config.apiKey}-${config.agentId}`;
|
|
42
39
|
const manager = wsManagerCache.get(cacheKey);
|
|
43
40
|
if (manager) {
|
|
44
|
-
|
|
41
|
+
logger.log(`🗑️ [WS-MANAGER-CACHE] Removing manager from cache: ${cacheKey}`);
|
|
45
42
|
manager.disconnect();
|
|
46
43
|
wsManagerCache.delete(cacheKey);
|
|
47
|
-
|
|
44
|
+
logger.log(`🗑️ [WS-MANAGER-CACHE] Manager removed, remaining managers: ${wsManagerCache.size}`);
|
|
48
45
|
}
|
|
49
46
|
else {
|
|
50
|
-
|
|
47
|
+
logger.log(`⚠️ [WS-MANAGER-CACHE] Manager not found in cache: ${cacheKey}`);
|
|
51
48
|
}
|
|
52
49
|
}
|
|
53
50
|
/**
|
|
54
51
|
* Clear all cached WebSocket managers.
|
|
55
52
|
*/
|
|
56
53
|
export function clearXYWebSocketManagers() {
|
|
57
|
-
|
|
58
|
-
log("Clearing all WebSocket manager caches");
|
|
54
|
+
logger.log("Clearing all WebSocket manager caches");
|
|
59
55
|
for (const manager of wsManagerCache.values()) {
|
|
60
56
|
manager.disconnect();
|
|
61
57
|
}
|
|
@@ -72,36 +68,35 @@ export function getCachedManagerCount() {
|
|
|
72
68
|
* Helps identify connection issues and orphan connections.
|
|
73
69
|
*/
|
|
74
70
|
export function diagnoseAllManagers() {
|
|
75
|
-
|
|
71
|
+
logger.log(`Total cached managers: ${wsManagerCache.size}`);
|
|
76
72
|
if (wsManagerCache.size === 0) {
|
|
77
|
-
|
|
73
|
+
logger.log("ℹ️ No managers in cache");
|
|
78
74
|
return;
|
|
79
75
|
}
|
|
80
76
|
let orphanCount = 0;
|
|
81
77
|
wsManagerCache.forEach((manager, key) => {
|
|
82
78
|
const diag = manager.getConnectionDiagnostics();
|
|
83
|
-
|
|
79
|
+
logger.log(` Total event listeners on manager: ${diag.totalEventListeners}`);
|
|
84
80
|
// Connection
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
81
|
+
logger.log(` 🔌 Connection:`);
|
|
82
|
+
logger.log(` - Exists: ${diag.connection.exists}`);
|
|
83
|
+
logger.log(` - ReadyState: ${diag.connection.readyState}`);
|
|
84
|
+
logger.log(` - State connected/ready: ${diag.connection.stateConnected}/${diag.connection.stateReady}`);
|
|
85
|
+
logger.log(` - Reconnect attempts: ${diag.connection.reconnectAttempts}`);
|
|
86
|
+
logger.log(` - Listeners on WebSocket: ${diag.connection.listenerCount}`);
|
|
87
|
+
logger.log(` - Heartbeat active: ${diag.connection.heartbeatActive}`);
|
|
88
|
+
logger.log(` - Has reconnect timer: ${diag.connection.hasReconnectTimer}`);
|
|
93
89
|
if (diag.connection.isOrphan) {
|
|
94
|
-
|
|
90
|
+
logger.log(` ⚠️ ORPHAN CONNECTION DETECTED!`);
|
|
95
91
|
orphanCount++;
|
|
96
92
|
}
|
|
97
|
-
console.log("");
|
|
98
93
|
});
|
|
99
94
|
if (orphanCount > 0) {
|
|
100
|
-
|
|
101
|
-
|
|
95
|
+
logger.log(`⚠️ Total orphan connections found: ${orphanCount}`);
|
|
96
|
+
logger.log(`💡 Suggestion: These connections should be cleaned up`);
|
|
102
97
|
}
|
|
103
98
|
else {
|
|
104
|
-
|
|
99
|
+
logger.log(`✅ No orphan connections found`);
|
|
105
100
|
}
|
|
106
101
|
}
|
|
107
102
|
/**
|
|
@@ -113,13 +108,13 @@ export function cleanupOrphanConnections() {
|
|
|
113
108
|
wsManagerCache.forEach((manager, key) => {
|
|
114
109
|
const diag = manager.getConnectionDiagnostics();
|
|
115
110
|
if (diag.connection.isOrphan) {
|
|
116
|
-
|
|
111
|
+
logger.log(`🧹 Cleaning up orphan connections in manager: ${key}`);
|
|
117
112
|
manager.disconnect();
|
|
118
113
|
cleanedCount++;
|
|
119
114
|
}
|
|
120
115
|
});
|
|
121
116
|
if (cleanedCount > 0) {
|
|
122
|
-
|
|
117
|
+
logger.log(`🧹 Cleaned up ${cleanedCount} manager(s) with orphan connections`);
|
|
123
118
|
}
|
|
124
119
|
return cleanedCount;
|
|
125
120
|
}
|
|
@@ -4,12 +4,13 @@ import { URL } from "node:url";
|
|
|
4
4
|
import { randomBytes } from "node:crypto";
|
|
5
5
|
import { getCsplConfig } from "./config.js";
|
|
6
6
|
import { DEFAULT_HTTP_PORT, HTTP_STATUS_BAD_REQUEST } from "./constants.js";
|
|
7
|
+
import { logger } from "../utils/logger.js";
|
|
7
8
|
function generateTraceId() {
|
|
8
9
|
return randomBytes(16).toString("hex");
|
|
9
10
|
}
|
|
10
11
|
function buildHeaders(config) {
|
|
11
12
|
const traceId = generateTraceId();
|
|
12
|
-
|
|
13
|
+
logger.log(`[SENTINEL HOOK] trace-id: ${traceId}`);
|
|
13
14
|
return {
|
|
14
15
|
"x-hag-trace-id": traceId,
|
|
15
16
|
"x-uid": config.uid,
|
|
@@ -65,21 +66,21 @@ export async function callCsplApi(questionText, cfg) {
|
|
|
65
66
|
res.on("end", () => {
|
|
66
67
|
try {
|
|
67
68
|
const result = parseResponse(data);
|
|
68
|
-
|
|
69
|
+
logger.log(`[SENTINEL HOOK] ✅ 请求成功`);
|
|
69
70
|
resolve(result);
|
|
70
71
|
}
|
|
71
72
|
catch (e) {
|
|
72
|
-
|
|
73
|
+
logger.error(`[SENTINEL HOOK] ❌ 请求失败: ${e instanceof Error ? e.message : String(e)}`);
|
|
73
74
|
reject(e);
|
|
74
75
|
}
|
|
75
76
|
});
|
|
76
77
|
});
|
|
77
78
|
req.on("error", (error) => {
|
|
78
|
-
|
|
79
|
+
logger.error(`[SENTINEL HOOK] ❌ 请求错误: ${error instanceof Error ? error.message : String(error)}`);
|
|
79
80
|
reject(error);
|
|
80
81
|
});
|
|
81
82
|
req.on("timeout", () => {
|
|
82
|
-
|
|
83
|
+
logger.error(`[SENTINEL HOOK] ⏰ 请求超时 (${config.api.timeout}ms)`);
|
|
83
84
|
req.destroy();
|
|
84
85
|
reject(new Error("[SENTINEL HOOK] Request timeout"));
|
|
85
86
|
});
|
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
import fetch from "node-fetch";
|
|
3
3
|
import fs from "fs/promises";
|
|
4
4
|
import path from "path";
|
|
5
|
+
import { logger } from "./utils/logger.js";
|
|
5
6
|
/**
|
|
6
7
|
* Download a file from URL to local path.
|
|
7
8
|
*/
|
|
@@ -19,10 +20,10 @@ export async function downloadFile(url, destPath) {
|
|
|
19
20
|
}
|
|
20
21
|
catch (error) {
|
|
21
22
|
if (error.name === 'AbortError') {
|
|
22
|
-
|
|
23
|
+
logger.error(`Download timeout (30s) for ${url}`);
|
|
23
24
|
throw new Error(`Download timeout after 30 seconds`);
|
|
24
25
|
}
|
|
25
|
-
|
|
26
|
+
logger.error(`Failed to download file from ${url}:`);
|
|
26
27
|
throw error;
|
|
27
28
|
}
|
|
28
29
|
finally {
|
|
@@ -51,7 +52,7 @@ export async function downloadFilesFromParts(fileParts, tempDir = "/tmp/xy_chann
|
|
|
51
52
|
});
|
|
52
53
|
}
|
|
53
54
|
catch (error) {
|
|
54
|
-
|
|
55
|
+
logger.error(`Failed to download file ${name}:`);
|
|
55
56
|
// Continue with other files
|
|
56
57
|
}
|
|
57
58
|
}
|
package/dist/src/file-upload.js
CHANGED
|
@@ -3,13 +3,14 @@
|
|
|
3
3
|
import fetch from "node-fetch";
|
|
4
4
|
import fs from "fs/promises";
|
|
5
5
|
import os from "os";
|
|
6
|
+
import { logger } from "./utils/logger.js";
|
|
6
7
|
import path from "path";
|
|
7
8
|
import { calculateSHA256 } from "./utils/crypto.js";
|
|
8
9
|
function isRemoteUrl(filePath) {
|
|
9
10
|
return filePath.startsWith("http://") || filePath.startsWith("https://");
|
|
10
11
|
}
|
|
11
12
|
async function downloadToTempFile(url) {
|
|
12
|
-
|
|
13
|
+
logger.log(`[XY File Upload] Downloading remote file: ${url}`);
|
|
13
14
|
const response = await fetch(url);
|
|
14
15
|
if (!response.ok) {
|
|
15
16
|
throw new Error(`Failed to download remote file: HTTP ${response.status}`);
|
|
@@ -18,7 +19,7 @@ async function downloadToTempFile(url) {
|
|
|
18
19
|
const urlFileName = path.basename(new URL(url).pathname) || "download";
|
|
19
20
|
const tempPath = path.join(os.tmpdir(), `xy-upload-${Date.now()}-${urlFileName}`);
|
|
20
21
|
await fs.writeFile(tempPath, buffer);
|
|
21
|
-
|
|
22
|
+
logger.log(`[XY File Upload] Downloaded to temp file: ${tempPath}`);
|
|
22
23
|
return tempPath;
|
|
23
24
|
}
|
|
24
25
|
/**
|
|
@@ -39,7 +40,7 @@ export class XYFileUploadService {
|
|
|
39
40
|
* Returns the objectId (as fileId) for use in A2A messages.
|
|
40
41
|
*/
|
|
41
42
|
async uploadFile(filePath, objectType = "TEMPORARY_MATERIAL_DOC") {
|
|
42
|
-
|
|
43
|
+
logger.log(`[XY File Upload] Starting file upload: ${filePath}`);
|
|
43
44
|
let localFilePath = filePath;
|
|
44
45
|
let isTempFile = false;
|
|
45
46
|
try {
|
|
@@ -54,7 +55,7 @@ export class XYFileUploadService {
|
|
|
54
55
|
const fileSha256 = calculateSHA256(fileBuffer);
|
|
55
56
|
const fileSize = fileBuffer.length;
|
|
56
57
|
// Phase 1: Prepare
|
|
57
|
-
|
|
58
|
+
logger.log(`[XY File Upload] Phase 1: Prepare upload for ${fileName}`);
|
|
58
59
|
const prepareResp = await fetch(`${this.baseUrl}/osms/v1/file/manager/prepare`, {
|
|
59
60
|
method: "POST",
|
|
60
61
|
headers: {
|
|
@@ -84,7 +85,7 @@ export class XYFileUploadService {
|
|
|
84
85
|
}
|
|
85
86
|
const { objectId, draftId, uploadInfos } = prepareData;
|
|
86
87
|
// Phase 2: Upload
|
|
87
|
-
|
|
88
|
+
logger.log(`[XY File Upload] Phase 2: Upload file data`);
|
|
88
89
|
const uploadInfo = uploadInfos[0]; // Single-part upload
|
|
89
90
|
const uploadResp = await fetch(uploadInfo.url, {
|
|
90
91
|
method: uploadInfo.method,
|
|
@@ -95,9 +96,9 @@ export class XYFileUploadService {
|
|
|
95
96
|
const uploadErrorText = await uploadResp.text();
|
|
96
97
|
throw new Error(`Upload failed: HTTP ${uploadResp.status}`);
|
|
97
98
|
}
|
|
98
|
-
|
|
99
|
+
logger.log(`[XY File Upload] Upload complete`);
|
|
99
100
|
// Phase 3: Complete
|
|
100
|
-
|
|
101
|
+
logger.log(`[XY File Upload] Phase 3: Complete upload`);
|
|
101
102
|
const completeResp = await fetch(`${this.baseUrl}/osms/v1/file/manager/complete`, {
|
|
102
103
|
method: "POST",
|
|
103
104
|
headers: {
|
|
@@ -115,11 +116,11 @@ export class XYFileUploadService {
|
|
|
115
116
|
throw new Error(`Complete failed: HTTP ${completeResp.status}`);
|
|
116
117
|
}
|
|
117
118
|
const completeData = await completeResp.json();
|
|
118
|
-
|
|
119
|
+
logger.log(`[XY File Upload] File upload successful: ${fileName} → objectId=${objectId}`);
|
|
119
120
|
return objectId;
|
|
120
121
|
}
|
|
121
122
|
catch (error) {
|
|
122
|
-
|
|
123
|
+
logger.error(`[XY File Upload] File upload failed for ${filePath}:`, error);
|
|
123
124
|
throw error;
|
|
124
125
|
}
|
|
125
126
|
finally {
|
|
@@ -150,7 +151,7 @@ export class XYFileUploadService {
|
|
|
150
151
|
const fileSha256 = calculateSHA256(fileBuffer);
|
|
151
152
|
const fileSize = fileBuffer.length;
|
|
152
153
|
// Phase 1: Prepare
|
|
153
|
-
|
|
154
|
+
logger.log(`[XY File Upload] Phase 1: Prepare upload for ${fileName}`);
|
|
154
155
|
const prepareResp = await fetch(`${this.baseUrl}/osms/v1/file/manager/prepare`, {
|
|
155
156
|
method: "POST",
|
|
156
157
|
headers: {
|
|
@@ -179,23 +180,23 @@ export class XYFileUploadService {
|
|
|
179
180
|
throw new Error(`Prepare failed: ${prepareData.desc}`);
|
|
180
181
|
}
|
|
181
182
|
const { objectId, draftId, uploadInfos } = prepareData;
|
|
182
|
-
|
|
183
|
+
logger.log(`[XY File Upload] Prepare complete: objectId=${objectId}, draftId=${draftId}`);
|
|
183
184
|
// Phase 2: Upload
|
|
184
|
-
|
|
185
|
+
logger.log(`[XY File Upload] Phase 2: Upload file data`);
|
|
185
186
|
const uploadInfo = uploadInfos[0]; // Single-part upload
|
|
186
187
|
const uploadResp = await fetch(uploadInfo.url, {
|
|
187
188
|
method: uploadInfo.method,
|
|
188
189
|
headers: uploadInfo.headers,
|
|
189
190
|
body: fileBuffer,
|
|
190
191
|
});
|
|
191
|
-
|
|
192
|
+
logger.log(`[XY File Upload] Upload response status: ${uploadResp.status}`);
|
|
192
193
|
if (!uploadResp.ok) {
|
|
193
194
|
const uploadErrorText = await uploadResp.text();
|
|
194
195
|
throw new Error(`Upload failed: HTTP ${uploadResp.status}`);
|
|
195
196
|
}
|
|
196
|
-
|
|
197
|
+
logger.log(`[XY File Upload] Upload complete`);
|
|
197
198
|
// Phase 3: CompleteAndQuery - get file URL
|
|
198
|
-
|
|
199
|
+
logger.log(`[XY File Upload] Phase 3: CompleteAndQuery to get file URL`);
|
|
199
200
|
const completeResp = await fetch(`${this.baseUrl}/osms/v1/file/manager/completeAndQuery`, {
|
|
200
201
|
method: "POST",
|
|
201
202
|
headers: {
|
|
@@ -218,11 +219,11 @@ export class XYFileUploadService {
|
|
|
218
219
|
if (!fileUrl) {
|
|
219
220
|
throw new Error("No file URL returned from completeAndQuery");
|
|
220
221
|
}
|
|
221
|
-
|
|
222
|
+
logger.log(`[XY File Upload] File upload successful`);
|
|
222
223
|
return fileUrl;
|
|
223
224
|
}
|
|
224
225
|
catch (error) {
|
|
225
|
-
|
|
226
|
+
logger.error(`[XY File Upload] File upload with URL retrieval failed for ${filePath}:`, error);
|
|
226
227
|
throw error;
|
|
227
228
|
}
|
|
228
229
|
finally {
|
|
@@ -249,7 +250,7 @@ export class XYFileUploadService {
|
|
|
249
250
|
});
|
|
250
251
|
}
|
|
251
252
|
catch (error) {
|
|
252
|
-
|
|
253
|
+
logger.error(`[XY File Upload] Failed to upload ${filePath}, skipping:`, error);
|
|
253
254
|
// Continue with other files
|
|
254
255
|
}
|
|
255
256
|
}
|
package/dist/src/formatter.js
CHANGED
|
@@ -1,15 +1,13 @@
|
|
|
1
1
|
// OpenClaw → A2A format conversion
|
|
2
2
|
import { v4 as uuidv4 } from "uuid";
|
|
3
3
|
import { getXYWebSocketManager } from "./client.js";
|
|
4
|
-
import {
|
|
4
|
+
import { logger } from "./utils/logger.js";
|
|
5
|
+
import { getCurrentTaskId, getCurrentMessageId } from "./task-manager.js";
|
|
5
6
|
/**
|
|
6
7
|
* Send an A2A artifact update response.
|
|
7
8
|
*/
|
|
8
9
|
export async function sendA2AResponse(params) {
|
|
9
10
|
const { config, sessionId, taskId, messageId, text, append, final, files, errorCode, errorMessage } = params;
|
|
10
|
-
const runtime = getXYRuntime();
|
|
11
|
-
const log = runtime?.log ?? console.log;
|
|
12
|
-
const errorFn = runtime?.error ?? console.error;
|
|
13
11
|
// Build artifact update event
|
|
14
12
|
const artifact = {
|
|
15
13
|
taskId,
|
|
@@ -48,7 +46,7 @@ export async function sendA2AResponse(params) {
|
|
|
48
46
|
code: errorCode,
|
|
49
47
|
message: errorMessage ?? "任务执行异常,请重试",
|
|
50
48
|
};
|
|
51
|
-
log(`[A2A_RESPONSE] ⚠️ Including error code: ${errorCode}`);
|
|
49
|
+
logger.log(`[A2A_RESPONSE] ⚠️ Including error code: ${errorCode}`);
|
|
52
50
|
}
|
|
53
51
|
// Send via WebSocket
|
|
54
52
|
const wsManager = getXYWebSocketManager(config);
|
|
@@ -60,13 +58,13 @@ export async function sendA2AResponse(params) {
|
|
|
60
58
|
msgDetail: JSON.stringify(jsonRpcResponse),
|
|
61
59
|
};
|
|
62
60
|
// 📋 Log complete response body
|
|
63
|
-
log(`[A2A_RESPONSE] 📤 Sending A2A artifact-update response: taskId: ${taskId}`);
|
|
64
|
-
log(`[A2A_RESPONSE] - append: ${append}`);
|
|
65
|
-
log(`[A2A_RESPONSE] - final: ${final}`);
|
|
66
|
-
log(`[A2A_RESPONSE] - text: ${text.length <= 10 ? text : text.slice(0, 5) + '***' + text.slice(-5)}`);
|
|
67
|
-
log(`[A2A_RESPONSE] - files count: ${files?.length ?? 0}`);
|
|
61
|
+
logger.log(`[A2A_RESPONSE] 📤 Sending A2A artifact-update response: taskId: ${taskId}`);
|
|
62
|
+
logger.log(`[A2A_RESPONSE] - append: ${append}`);
|
|
63
|
+
logger.log(`[A2A_RESPONSE] - final: ${final}`);
|
|
64
|
+
logger.log(`[A2A_RESPONSE] - text: ${text.length <= 10 ? text : text.slice(0, 5) + '***' + text.slice(-5)}`);
|
|
65
|
+
logger.log(`[A2A_RESPONSE] - files count: ${files?.length ?? 0}`);
|
|
68
66
|
await wsManager.sendMessage(sessionId, outboundMessage);
|
|
69
|
-
log(`[A2A_RESPONSE] ✅ Message sent successfully`);
|
|
67
|
+
logger.log(`[A2A_RESPONSE] ✅ Message sent successfully`);
|
|
70
68
|
}
|
|
71
69
|
/**
|
|
72
70
|
* Send an A2A artifact-update with reasoningText part.
|
|
@@ -75,9 +73,6 @@ export async function sendA2AResponse(params) {
|
|
|
75
73
|
*/
|
|
76
74
|
export async function sendReasoningTextUpdate(params) {
|
|
77
75
|
const { config, sessionId, taskId, messageId, text, append = true } = params;
|
|
78
|
-
const runtime = getXYRuntime();
|
|
79
|
-
const log = runtime?.log ?? console.log;
|
|
80
|
-
const error = runtime?.error ?? console.error;
|
|
81
76
|
const artifact = {
|
|
82
77
|
taskId,
|
|
83
78
|
kind: "artifact-update",
|
|
@@ -115,12 +110,13 @@ export async function sendReasoningTextUpdate(params) {
|
|
|
115
110
|
*/
|
|
116
111
|
export async function sendStatusUpdate(params) {
|
|
117
112
|
const { config, sessionId, taskId, messageId, text, state } = params;
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
const
|
|
113
|
+
// Dynamic lookup: use latest taskId/messageId from task-manager (handles steer/interrupt),
|
|
114
|
+
// fall back to closure-captured values
|
|
115
|
+
const currentTaskId = getCurrentTaskId(sessionId) ?? taskId;
|
|
116
|
+
const currentMessageId = getCurrentMessageId(sessionId) ?? messageId;
|
|
121
117
|
// Build status update event following A2A protocol standard
|
|
122
118
|
const statusUpdate = {
|
|
123
|
-
taskId,
|
|
119
|
+
taskId: currentTaskId,
|
|
124
120
|
kind: "status-update",
|
|
125
121
|
final: false, // Status updates should not end the stream
|
|
126
122
|
status: {
|
|
@@ -139,7 +135,7 @@ export async function sendStatusUpdate(params) {
|
|
|
139
135
|
// Build JSON-RPC response
|
|
140
136
|
const jsonRpcResponse = {
|
|
141
137
|
jsonrpc: "2.0",
|
|
142
|
-
id:
|
|
138
|
+
id: currentMessageId,
|
|
143
139
|
result: statusUpdate,
|
|
144
140
|
};
|
|
145
141
|
// Send via WebSocket
|
|
@@ -148,13 +144,13 @@ export async function sendStatusUpdate(params) {
|
|
|
148
144
|
msgType: "agent_response",
|
|
149
145
|
agentId: config.agentId,
|
|
150
146
|
sessionId,
|
|
151
|
-
taskId,
|
|
147
|
+
taskId: currentTaskId,
|
|
152
148
|
msgDetail: JSON.stringify(jsonRpcResponse),
|
|
153
149
|
};
|
|
154
150
|
// 📋 Log complete response body
|
|
155
|
-
log(`[A2A_STATUS] 📤 Sending A2A status-update:`);
|
|
156
|
-
log(`[A2A_STATUS] - taskId: ${
|
|
157
|
-
log(`[A2A_STATUS] - text: "${text}"`);
|
|
151
|
+
logger.log(`[A2A_STATUS] 📤 Sending A2A status-update:`);
|
|
152
|
+
logger.log(`[A2A_STATUS] - taskId: ${currentTaskId}`);
|
|
153
|
+
logger.log(`[A2A_STATUS] - text: "${text}"`);
|
|
158
154
|
await wsManager.sendMessage(sessionId, outboundMessage);
|
|
159
155
|
}
|
|
160
156
|
/**
|
|
@@ -162,13 +158,14 @@ export async function sendStatusUpdate(params) {
|
|
|
162
158
|
*/
|
|
163
159
|
export async function sendCommand(params) {
|
|
164
160
|
const { config, sessionId, taskId, messageId, command } = params;
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
const
|
|
161
|
+
// Dynamic lookup: use latest taskId/messageId from task-manager (handles steer/interrupt),
|
|
162
|
+
// fall back to closure-captured values
|
|
163
|
+
const currentTaskId = getCurrentTaskId(sessionId) ?? taskId;
|
|
164
|
+
const currentMessageId = getCurrentMessageId(sessionId) ?? messageId;
|
|
168
165
|
// Build artifact update with command as data
|
|
169
166
|
// Wrap command in commands array as per protocol requirement
|
|
170
167
|
const artifact = {
|
|
171
|
-
taskId,
|
|
168
|
+
taskId: currentTaskId,
|
|
172
169
|
kind: "artifact-update",
|
|
173
170
|
append: false,
|
|
174
171
|
lastChunk: true,
|
|
@@ -188,7 +185,7 @@ export async function sendCommand(params) {
|
|
|
188
185
|
// Build JSON-RPC response
|
|
189
186
|
const jsonRpcResponse = {
|
|
190
187
|
jsonrpc: "2.0",
|
|
191
|
-
id:
|
|
188
|
+
id: currentMessageId,
|
|
192
189
|
result: artifact,
|
|
193
190
|
};
|
|
194
191
|
// Send via WebSocket
|
|
@@ -197,22 +194,19 @@ export async function sendCommand(params) {
|
|
|
197
194
|
msgType: "agent_response",
|
|
198
195
|
agentId: config.agentId,
|
|
199
196
|
sessionId,
|
|
200
|
-
taskId,
|
|
197
|
+
taskId: currentTaskId,
|
|
201
198
|
msgDetail: JSON.stringify(jsonRpcResponse),
|
|
202
199
|
};
|
|
203
200
|
// 📋 Log complete response body
|
|
204
|
-
log(`[A2A_COMMAND] 📤 Sending A2A command: taskId: ${
|
|
201
|
+
logger.log(`[A2A_COMMAND] 📤 Sending A2A command: taskId: ${currentTaskId}`);
|
|
205
202
|
await wsManager.sendMessage(sessionId, outboundMessage);
|
|
206
|
-
log(`[A2A_COMMAND] ✅ Command sent successfully`);
|
|
203
|
+
logger.log(`[A2A_COMMAND] ✅ Command sent successfully`);
|
|
207
204
|
}
|
|
208
205
|
/**
|
|
209
206
|
* Send a clearContext response.
|
|
210
207
|
*/
|
|
211
208
|
export async function sendClearContextResponse(params) {
|
|
212
209
|
const { config, sessionId, messageId } = params;
|
|
213
|
-
const runtime = getXYRuntime();
|
|
214
|
-
const log = runtime?.log ?? console.log;
|
|
215
|
-
const error = runtime?.error ?? console.error;
|
|
216
210
|
// Build JSON-RPC response for clearContext
|
|
217
211
|
const jsonRpcResponse = {
|
|
218
212
|
jsonrpc: "2.0",
|
|
@@ -238,16 +232,13 @@ export async function sendClearContextResponse(params) {
|
|
|
238
232
|
msgDetail: JSON.stringify(jsonRpcResponse),
|
|
239
233
|
};
|
|
240
234
|
await wsManager.sendMessage(sessionId, outboundMessage);
|
|
241
|
-
log(`Sent clearContext response: sessionId=${sessionId}`);
|
|
235
|
+
logger.log(`Sent clearContext response: sessionId=${sessionId}`);
|
|
242
236
|
}
|
|
243
237
|
/**
|
|
244
238
|
* Send a tasks/cancel response.
|
|
245
239
|
*/
|
|
246
240
|
export async function sendTasksCancelResponse(params) {
|
|
247
241
|
const { config, sessionId, taskId, messageId } = params;
|
|
248
|
-
const runtime = getXYRuntime();
|
|
249
|
-
const log = runtime?.log ?? console.log;
|
|
250
|
-
const error = runtime?.error ?? console.error;
|
|
251
242
|
// Build JSON-RPC response for tasks/cancel
|
|
252
243
|
// Note: Using any to bypass type check as the response format differs from standard A2A types
|
|
253
244
|
const jsonRpcResponse = {
|
|
@@ -274,16 +265,13 @@ export async function sendTasksCancelResponse(params) {
|
|
|
274
265
|
msgDetail: JSON.stringify(jsonRpcResponse),
|
|
275
266
|
};
|
|
276
267
|
await wsManager.sendMessage(sessionId, outboundMessage);
|
|
277
|
-
log(`Sent tasks/cancel response: sessionId=${sessionId}, taskId=${taskId}`);
|
|
268
|
+
logger.log(`Sent tasks/cancel response: sessionId=${sessionId}, taskId=${taskId}`);
|
|
278
269
|
}
|
|
279
270
|
/**
|
|
280
271
|
* Send a Trigger response with pushData content.
|
|
281
272
|
*/
|
|
282
273
|
export async function sendTriggerResponse(params) {
|
|
283
274
|
const { config, sessionId, taskId, messageId, content } = params;
|
|
284
|
-
const runtime = getXYRuntime();
|
|
285
|
-
const log = runtime?.log ?? console.log;
|
|
286
|
-
const error = runtime?.error ?? console.error;
|
|
287
275
|
// Build JSON-RPC response for Trigger
|
|
288
276
|
const jsonRpcResponse = {
|
|
289
277
|
jsonrpc: "2.0",
|
|
@@ -318,7 +306,7 @@ export async function sendTriggerResponse(params) {
|
|
|
318
306
|
taskId,
|
|
319
307
|
msgDetail: JSON.stringify(jsonRpcResponse),
|
|
320
308
|
};
|
|
321
|
-
log(`[TRIGGER_RESPONSE] Sending Trigger response: sessionId=${sessionId}, taskId=${taskId}`);
|
|
309
|
+
logger.log(`[TRIGGER_RESPONSE] Sending Trigger response: sessionId=${sessionId}, taskId=${taskId}`);
|
|
322
310
|
await wsManager.sendMessage(sessionId, outboundMessage);
|
|
323
|
-
log(`[TRIGGER_RESPONSE] Trigger response sent successfully`);
|
|
311
|
+
logger.log(`[TRIGGER_RESPONSE] Trigger response sent successfully`);
|
|
324
312
|
}
|
package/dist/src/heartbeat.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
// Heartbeat management for WebSocket connections
|
|
2
2
|
import WebSocket from "ws";
|
|
3
|
+
import { logger } from "./utils/logger.js";
|
|
3
4
|
/**
|
|
4
5
|
* Manages heartbeat for a WebSocket connection.
|
|
5
6
|
* Supports both application-level (30s) and protocol-level (20s) heartbeats.
|
|
@@ -23,8 +24,8 @@ export class HeartbeatManager {
|
|
|
23
24
|
this.onTimeout = onTimeout;
|
|
24
25
|
this.serverName = serverName;
|
|
25
26
|
this.onHeartbeatSuccess = onHeartbeatSuccess;
|
|
26
|
-
this.log = logFn ??
|
|
27
|
-
this.error = errorFn ??
|
|
27
|
+
this.log = logFn ?? ((msg, ...args) => logger.log(msg, ...args));
|
|
28
|
+
this.error = errorFn ?? ((msg, ...args) => logger.error(msg, ...args));
|
|
28
29
|
}
|
|
29
30
|
/**
|
|
30
31
|
* Start heartbeat monitoring.
|
|
@@ -67,7 +68,7 @@ export class HeartbeatManager {
|
|
|
67
68
|
*/
|
|
68
69
|
sendHeartbeat() {
|
|
69
70
|
if (this.ws.readyState !== WebSocket.OPEN) {
|
|
70
|
-
|
|
71
|
+
this.log(`Cannot send heartbeat for ${this.serverName}: WebSocket not open`);
|
|
71
72
|
return;
|
|
72
73
|
}
|
|
73
74
|
try {
|
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
// 监听 LoginTokenEvent.ClawAutoLogin 事件,将 clientId 写入 .xiaoyitoken.json
|
|
3
3
|
import { readFileSync, writeFileSync, existsSync, mkdirSync } from "fs";
|
|
4
4
|
import { dirname } from "path";
|
|
5
|
+
import { logger } from "./utils/logger.js";
|
|
5
6
|
const TOKEN_FILE_PATH = "/home/sandbox/.openclaw/.xiaoyitoken.json";
|
|
6
7
|
/**
|
|
7
8
|
* 处理 LoginTokenEvent.ClawAutoLogin 事件
|
|
@@ -11,15 +12,13 @@ const TOKEN_FILE_PATH = "/home/sandbox/.openclaw/.xiaoyitoken.json";
|
|
|
11
12
|
* @param runtime - 运行时环境
|
|
12
13
|
*/
|
|
13
14
|
export function handleLoginTokenEvent(context, runtime) {
|
|
14
|
-
const log = runtime?.log ?? console.log;
|
|
15
|
-
const error = runtime?.error ?? console.error;
|
|
16
15
|
try {
|
|
17
16
|
const clientId = context.event?.payload?.clientId;
|
|
18
17
|
if (!clientId || typeof clientId !== "string") {
|
|
19
|
-
error("[LOGIN_TOKEN_HANDLER] invalid payload: missing clientId");
|
|
18
|
+
logger.error("[LOGIN_TOKEN_HANDLER] invalid payload: missing clientId");
|
|
20
19
|
return;
|
|
21
20
|
}
|
|
22
|
-
log(`[LOGIN_TOKEN_HANDLER] received login token event, clientId=${clientId}`);
|
|
21
|
+
logger.log(`[LOGIN_TOKEN_HANDLER] received login token event, clientId=${clientId}`);
|
|
23
22
|
// Ensure directory exists
|
|
24
23
|
const dir = dirname(TOKEN_FILE_PATH);
|
|
25
24
|
if (!existsSync(dir)) {
|
|
@@ -44,17 +43,17 @@ export function handleLoginTokenEvent(context, runtime) {
|
|
|
44
43
|
if (existing) {
|
|
45
44
|
// Update timestamp
|
|
46
45
|
existing.timestamp = now;
|
|
47
|
-
log(`[LOGIN_TOKEN_HANDLER] updated timestamp for clientId=${clientId}`);
|
|
46
|
+
logger.log(`[LOGIN_TOKEN_HANDLER] updated timestamp for clientId=${clientId}`);
|
|
48
47
|
}
|
|
49
48
|
else {
|
|
50
49
|
// Insert new entry
|
|
51
50
|
tokens.push({ clientId, timestamp: now });
|
|
52
|
-
log(`[LOGIN_TOKEN_HANDLER] inserted new entry for clientId=${clientId}`);
|
|
51
|
+
logger.log(`[LOGIN_TOKEN_HANDLER] inserted new entry for clientId=${clientId}`);
|
|
53
52
|
}
|
|
54
53
|
writeFileSync(TOKEN_FILE_PATH, JSON.stringify(tokens, null, 2), "utf-8");
|
|
55
|
-
log(`[LOGIN_TOKEN_HANDLER] wrote token file: ${TOKEN_FILE_PATH}`);
|
|
54
|
+
logger.log(`[LOGIN_TOKEN_HANDLER] wrote token file: ${TOKEN_FILE_PATH}`);
|
|
56
55
|
}
|
|
57
56
|
catch (err) {
|
|
58
|
-
error("[LOGIN_TOKEN_HANDLER] failed to handle event:", err);
|
|
57
|
+
logger.error("[LOGIN_TOKEN_HANDLER] failed to handle event:", err);
|
|
59
58
|
}
|
|
60
59
|
}
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { logger } from "./utils/logger.js";
|
|
1
2
|
const MAX_QUEUE_SIZE = 1000;
|
|
2
3
|
/**
|
|
3
4
|
* Simple message queue for buffering outbound WebSocket messages
|
|
@@ -7,7 +8,7 @@ export class MessageQueue {
|
|
|
7
8
|
items = [];
|
|
8
9
|
log;
|
|
9
10
|
constructor(log) {
|
|
10
|
-
this.log = log ??
|
|
11
|
+
this.log = log ?? ((msg, ...args) => logger.log(msg, ...args));
|
|
11
12
|
}
|
|
12
13
|
/** Enqueue a message. Drops oldest if over limit. */
|
|
13
14
|
enqueue(message) {
|