@ynhcj/xiaoyi-channel 0.0.125-beta → 0.0.126-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/bot.js CHANGED
@@ -200,7 +200,7 @@ export async function handleXYMessage(params) {
200
200
  const fileParts = extractFileParts(parsed.parts);
201
201
  // Download files to local disk
202
202
  const downloadedFiles = await downloadFilesFromParts(fileParts);
203
- console.log("Downloaded files:", JSON.stringify(downloadedFiles, null, 2));
203
+ log("Downloaded files:", JSON.stringify(downloadedFiles, null, 2));
204
204
  const mediaPayload = buildXYMediaPayload(downloadedFiles);
205
205
  // Resolve envelope format options (following feishu pattern)
206
206
  const envelopeOptions = core.channel.reply.resolveEnvelopeFormatOptions(cfg);
@@ -44,16 +44,17 @@ export function getXYWebSocketManager(config) {
44
44
  * Disconnects the manager and removes it from the cache.
45
45
  */
46
46
  export function removeXYWebSocketManager(config) {
47
+ const log = runtime?.log ?? console.log;
47
48
  const cacheKey = `${config.apiKey}-${config.agentId}`;
48
49
  const manager = wsManagerCache.get(cacheKey);
49
50
  if (manager) {
50
- console.log(`đŸ—‘ī¸ [WS-MANAGER-CACHE] Removing manager from cache: ${cacheKey}`);
51
+ log(`đŸ—‘ī¸ [WS-MANAGER-CACHE] Removing manager from cache: ${cacheKey}`);
51
52
  manager.disconnect();
52
53
  wsManagerCache.delete(cacheKey);
53
- console.log(`đŸ—‘ī¸ [WS-MANAGER-CACHE] Manager removed, remaining managers: ${wsManagerCache.size}`);
54
+ log(`đŸ—‘ī¸ [WS-MANAGER-CACHE] Manager removed, remaining managers: ${wsManagerCache.size}`);
54
55
  }
55
56
  else {
56
- console.log(`âš ī¸ [WS-MANAGER-CACHE] Manager not found in cache: ${cacheKey}`);
57
+ log(`âš ī¸ [WS-MANAGER-CACHE] Manager not found in cache: ${cacheKey}`);
57
58
  }
58
59
  }
59
60
  /**
@@ -78,36 +79,37 @@ export function getCachedManagerCount() {
78
79
  * Helps identify connection issues and orphan connections.
79
80
  */
80
81
  export function diagnoseAllManagers() {
81
- console.log(`Total cached managers: ${wsManagerCache.size}`);
82
+ const log = runtime?.log ?? console.log;
83
+ log(`Total cached managers: ${wsManagerCache.size}`);
82
84
  if (wsManagerCache.size === 0) {
83
- console.log("â„šī¸ No managers in cache");
85
+ log("â„šī¸ No managers in cache");
84
86
  return;
85
87
  }
86
88
  let orphanCount = 0;
87
89
  wsManagerCache.forEach((manager, key) => {
88
90
  const diag = manager.getConnectionDiagnostics();
89
- console.log(` Total event listeners on manager: ${diag.totalEventListeners}`);
91
+ log(` Total event listeners on manager: ${diag.totalEventListeners}`);
90
92
  // Connection
91
- console.log(` 🔌 Connection:`);
92
- console.log(` - Exists: ${diag.connection.exists}`);
93
- console.log(` - ReadyState: ${diag.connection.readyState}`);
94
- console.log(` - State connected/ready: ${diag.connection.stateConnected}/${diag.connection.stateReady}`);
95
- console.log(` - Reconnect attempts: ${diag.connection.reconnectAttempts}`);
96
- console.log(` - Listeners on WebSocket: ${diag.connection.listenerCount}`);
97
- console.log(` - Heartbeat active: ${diag.connection.heartbeatActive}`);
98
- console.log(` - Has reconnect timer: ${diag.connection.hasReconnectTimer}`);
93
+ log(` 🔌 Connection:`);
94
+ log(` - Exists: ${diag.connection.exists}`);
95
+ log(` - ReadyState: ${diag.connection.readyState}`);
96
+ log(` - State connected/ready: ${diag.connection.stateConnected}/${diag.connection.stateReady}`);
97
+ log(` - Reconnect attempts: ${diag.connection.reconnectAttempts}`);
98
+ log(` - Listeners on WebSocket: ${diag.connection.listenerCount}`);
99
+ log(` - Heartbeat active: ${diag.connection.heartbeatActive}`);
100
+ log(` - Has reconnect timer: ${diag.connection.hasReconnectTimer}`);
99
101
  if (diag.connection.isOrphan) {
100
- console.log(` âš ī¸ ORPHAN CONNECTION DETECTED!`);
102
+ log(` âš ī¸ ORPHAN CONNECTION DETECTED!`);
101
103
  orphanCount++;
102
104
  }
103
- console.log("");
105
+ log("");
104
106
  });
105
107
  if (orphanCount > 0) {
106
- console.log(`âš ī¸ Total orphan connections found: ${orphanCount}`);
107
- console.log(`💡 Suggestion: These connections should be cleaned up`);
108
+ log(`âš ī¸ Total orphan connections found: ${orphanCount}`);
109
+ log(`💡 Suggestion: These connections should be cleaned up`);
108
110
  }
109
111
  else {
110
- console.log(`✅ No orphan connections found`);
112
+ log(`✅ No orphan connections found`);
111
113
  }
112
114
  }
113
115
  /**
@@ -115,17 +117,18 @@ export function diagnoseAllManagers() {
115
117
  * Returns the number of managers that had orphan connections.
116
118
  */
117
119
  export function cleanupOrphanConnections() {
120
+ const log = runtime?.log ?? console.log;
118
121
  let cleanedCount = 0;
119
122
  wsManagerCache.forEach((manager, key) => {
120
123
  const diag = manager.getConnectionDiagnostics();
121
124
  if (diag.connection.isOrphan) {
122
- console.log(`🧹 Cleaning up orphan connections in manager: ${key}`);
125
+ log(`🧹 Cleaning up orphan connections in manager: ${key}`);
123
126
  manager.disconnect();
124
127
  cleanedCount++;
125
128
  }
126
129
  });
127
130
  if (cleanedCount > 0) {
128
- console.log(`🧹 Cleaned up ${cleanedCount} manager(s) with orphan connections`);
131
+ log(`🧹 Cleaned up ${cleanedCount} manager(s) with orphan connections`);
129
132
  }
130
133
  return cleanedCount;
131
134
  }
@@ -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
- console.log(`[SENTINEL HOOK] trace-id: ${traceId}`);
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
- console.log(`[SENTINEL HOOK] ✅ č¯ˇæą‚æˆåŠŸ`);
69
+ logger.log(`[SENTINEL HOOK] ✅ č¯ˇæą‚æˆåŠŸ`);
69
70
  resolve(result);
70
71
  }
71
72
  catch (e) {
72
- console.error(`[SENTINEL HOOK] ❌ č¯ˇæą‚å¤ąč´Ĩ: ${e instanceof Error ? e.message : String(e)}`);
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
- console.error(`[SENTINEL HOOK] ❌ č¯ˇæą‚é”™č¯¯: ${error instanceof Error ? error.message : String(error)}`);
79
+ logger.error(`[SENTINEL HOOK] ❌ č¯ˇæą‚é”™č¯¯: ${error instanceof Error ? error.message : String(error)}`);
79
80
  reject(error);
80
81
  });
81
82
  req.on("timeout", () => {
82
- console.error(`[SENTINEL HOOK] ⏰ č¯ˇæą‚čļ…æ—ļ (${config.api.timeout}ms)`);
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
- console.log(`Download timeout (30s) for ${url}`);
23
+ logger.log(`Download timeout (30s) for ${url}`);
23
24
  throw new Error(`Download timeout after 30 seconds`);
24
25
  }
25
- console.log(`Failed to download file from ${url}:`);
26
+ logger.log(`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
- console.log(`Failed to download file ${name}:`);
55
+ logger.log(`Failed to download file ${name}:`);
55
56
  // Continue with other files
56
57
  }
57
58
  }
@@ -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
- console.log(`[XY File Upload] Downloading remote file: ${url}`);
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
- console.log(`[XY File Upload] Downloaded to temp file: ${tempPath}`);
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
- console.log(`[XY File Upload] Starting file upload: ${filePath}`);
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
- console.log(`[XY File Upload] Phase 1: Prepare upload for ${fileName}`);
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
- console.log(`[XY File Upload] Phase 2: Upload file data`);
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
- console.log(`[XY File Upload] Upload complete`);
99
+ logger.log(`[XY File Upload] Upload complete`);
99
100
  // Phase 3: Complete
100
- console.log(`[XY File Upload] Phase 3: Complete upload`);
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
- console.log(`[XY File Upload] File upload successful: ${fileName} → objectId=${objectId}`);
119
+ logger.log(`[XY File Upload] File upload successful: ${fileName} → objectId=${objectId}`);
119
120
  return objectId;
120
121
  }
121
122
  catch (error) {
122
- console.error(`[XY File Upload] File upload failed for ${filePath}:`, error);
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
- console.log(`[XY File Upload] Phase 1: Prepare upload for ${fileName}`);
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
- console.log(`[XY File Upload] Prepare complete: objectId=${objectId}, draftId=${draftId}`);
183
+ logger.log(`[XY File Upload] Prepare complete: objectId=${objectId}, draftId=${draftId}`);
183
184
  // Phase 2: Upload
184
- console.log(`[XY File Upload] Phase 2: Upload file data`);
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
- console.log(`[XY File Upload] Upload response status: ${uploadResp.status}`);
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
- console.log(`[XY File Upload] Upload complete`);
197
+ logger.log(`[XY File Upload] Upload complete`);
197
198
  // Phase 3: CompleteAndQuery - get file URL
198
- console.log(`[XY File Upload] Phase 3: CompleteAndQuery to get file URL`);
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
- console.log(`[XY File Upload] File upload successful`);
222
+ logger.log(`[XY File Upload] File upload successful`);
222
223
  return fileUrl;
223
224
  }
224
225
  catch (error) {
225
- console.error(`[XY File Upload] File upload with URL retrieval failed for ${filePath}:`, error);
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
- console.error(`[XY File Upload] Failed to upload ${filePath}, skipping:`, error);
253
+ logger.error(`[XY File Upload] Failed to upload ${filePath}, skipping:`, error);
253
254
  // Continue with other files
254
255
  }
255
256
  }
@@ -67,7 +67,7 @@ export class HeartbeatManager {
67
67
  */
68
68
  sendHeartbeat() {
69
69
  if (this.ws.readyState !== WebSocket.OPEN) {
70
- console.warn(`Cannot send heartbeat for ${this.serverName}: WebSocket not open`);
70
+ this.log(`Cannot send heartbeat for ${this.serverName}: WebSocket not open`);
71
71
  return;
72
72
  }
73
73
  try {
@@ -1,5 +1,5 @@
1
1
  import { resolveXYConfig } from "./config.js";
2
- import { getXYWebSocketManager, diagnoseAllManagers, cleanupOrphanConnections, removeXYWebSocketManager } from "./client.js";
2
+ import { getXYWebSocketManager, setClientRuntime, diagnoseAllManagers, cleanupOrphanConnections, removeXYWebSocketManager } from "./client.js";
3
3
  import { handleXYMessage } from "./bot.js";
4
4
  import { parseA2AMessage } from "./parser.js";
5
5
  import { hasActiveTask, getAllActiveTaskBindings } from "./task-manager.js";
@@ -51,8 +51,10 @@ export async function monitorXYProvider(opts = {}) {
51
51
  opts.setStatus({ lastEventAt: Date.now(), lastInboundAt: Date.now() });
52
52
  }
53
53
  : undefined;
54
+ // ✅ Set runtime for WebSocket manager logging before creating/getting manager
55
+ setClientRuntime(runtime);
54
56
  // 🔍 Diagnose WebSocket managers before gateway start
55
- console.log("🔍 [DIAGNOSTICS] Checking WebSocket managers before gateway start...");
57
+ log("🔍 [DIAGNOSTICS] Checking WebSocket managers before gateway start...");
56
58
  diagnoseAllManagers();
57
59
  // Get WebSocket manager (cached)
58
60
  const wsManager = getXYWebSocketManager(account);
@@ -141,7 +143,7 @@ export async function monitorXYProvider(opts = {}) {
141
143
  opts.setStatus?.({ connected: true });
142
144
  };
143
145
  const disconnectedHandler = (serverId) => {
144
- console.warn(`XY gateway: ${serverId} disconnected`);
146
+ log(`XY gateway: ${serverId} disconnected`);
145
147
  loggedServers.delete(serverId);
146
148
  // ✅ Report disconnection status (only if all servers disconnected)
147
149
  if (loggedServers.size === 0) {
@@ -177,13 +179,13 @@ export async function monitorXYProvider(opts = {}) {
177
179
  const cleanup = () => {
178
180
  log("XY gateway: cleaning up...");
179
181
  // 🔍 Diagnose before cleanup
180
- console.log("🔍 [DIAGNOSTICS] Checking WebSocket managers before cleanup...");
182
+ log("🔍 [DIAGNOSTICS] Checking WebSocket managers before cleanup...");
181
183
  diagnoseAllManagers();
182
184
  // Stop health check interval
183
185
  if (healthCheckInterval) {
184
186
  clearInterval(healthCheckInterval);
185
187
  healthCheckInterval = null;
186
- console.log("â¸ī¸ Stopped periodic health check");
188
+ log("â¸ī¸ Stopped periodic health check");
187
189
  }
188
190
  // Remove event handlers to prevent duplicate calls on gateway restart
189
191
  wsManager.off("message", messageHandler);
@@ -205,7 +207,7 @@ export async function monitorXYProvider(opts = {}) {
205
207
  activeMessages.clear();
206
208
  log(`[MONITOR-HANDLER] 🧹 Cleanup complete, cleared active messages and sessions`);
207
209
  // 🔍 Diagnose after cleanup
208
- console.log("🔍 [DIAGNOSTICS] Checking WebSocket managers after cleanup...");
210
+ log("🔍 [DIAGNOSTICS] Checking WebSocket managers after cleanup...");
209
211
  diagnoseAllManagers();
210
212
  };
211
213
  const handleAbort = async () => {
@@ -258,20 +260,20 @@ export async function monitorXYProvider(opts = {}) {
258
260
  wsManager.on("self-evolution-state-get-event", selfEvolutionStateGetHandler);
259
261
  wsManager.on("login-token-event", loginTokenEventHandler);
260
262
  // Start periodic health check (every 6 hours)
261
- console.log("đŸĨ Starting periodic health check (every 6 hours)...");
263
+ log("đŸĨ Starting periodic health check (every 6 hours)...");
262
264
  healthCheckInterval = setInterval(() => {
263
- console.log("đŸĨ [HEALTH CHECK] Periodic WebSocket diagnostics...");
265
+ log("đŸĨ [HEALTH CHECK] Periodic WebSocket diagnostics...");
264
266
  diagnoseAllManagers();
265
267
  // Auto-cleanup orphan connections
266
268
  const cleaned = cleanupOrphanConnections();
267
269
  if (cleaned > 0) {
268
- console.log(`🧹 [HEALTH CHECK] Auto-cleaned ${cleaned} manager(s) with orphan connections`);
270
+ log(`🧹 [HEALTH CHECK] Auto-cleaned ${cleaned} manager(s) with orphan connections`);
269
271
  }
270
272
  // Cleanup stale sessions (older than 10min TTL)
271
273
  const cleanedSessions = cleanupStaleSessions();
272
274
  const remainingSessions = getActiveSessionCount();
273
275
  if (cleanedSessions > 0 || remainingSessions > 0) {
274
- console.log(`🧹 [HEALTH CHECK] Sessions: cleaned=${cleanedSessions}, active=${remainingSessions}`);
276
+ log(`🧹 [HEALTH CHECK] Sessions: cleaned=${cleanedSessions}, active=${remainingSessions}`);
275
277
  }
276
278
  // Cleanup stale temp files (older than 24 hours)
277
279
  void cleanupStaleTempFiles();
@@ -4,6 +4,7 @@ import { XYPushService } from "./push.js";
4
4
  import { getCurrentSessionContext } from "./tools/session-manager.js";
5
5
  import { savePushData } from "./utils/pushdata-manager.js";
6
6
  import { getAllPushIds } from "./utils/pushid-manager.js";
7
+ import { logger } from "./utils/logger.js";
7
8
  // Special marker for default push delivery when no target is specified
8
9
  const DEFAULT_PUSH_MARKER = "default";
9
10
  // File extension to MIME type mapping
@@ -57,7 +58,7 @@ export const xyOutbound = {
57
58
  resolveTarget: ({ cfg, to, accountId, mode }) => {
58
59
  // If no target provided, use default marker for push delivery
59
60
  if (!to || to.trim() === "") {
60
- console.log(`[xyOutbound.resolveTarget] No target specified, using default push marker`);
61
+ logger.log(`[xyOutbound.resolveTarget] No target specified, using default push marker`);
61
62
  return {
62
63
  ok: true,
63
64
  to: DEFAULT_PUSH_MARKER,
@@ -66,24 +67,24 @@ export const xyOutbound = {
66
67
  const trimmedTo = to.trim();
67
68
  // If the target doesn't contain "::", try to enhance it with taskId from session context
68
69
  if (!trimmedTo.includes("::")) {
69
- console.log(`[xyOutbound.resolveTarget] Target "${trimmedTo}" missing taskId, looking up session context`);
70
+ logger.log(`[xyOutbound.resolveTarget] Target "${trimmedTo}" missing taskId, looking up session context`);
70
71
  // Try to get the current session context
71
72
  const sessionContext = getCurrentSessionContext();
72
73
  if (sessionContext && sessionContext.sessionId === trimmedTo) {
73
74
  const enhancedTarget = `${trimmedTo}::${sessionContext.taskId}`;
74
- console.log(`[xyOutbound.resolveTarget] Enhanced target: ${enhancedTarget}`);
75
+ logger.log(`[xyOutbound.resolveTarget] Enhanced target: ${enhancedTarget}`);
75
76
  return {
76
77
  ok: true,
77
78
  to: enhancedTarget,
78
79
  };
79
80
  }
80
81
  else {
81
- console.log(`[xyOutbound.resolveTarget] Could not find matching session context for "${trimmedTo}"`);
82
+ logger.log(`[xyOutbound.resolveTarget] Could not find matching session context for "${trimmedTo}"`);
82
83
  // Still return the original target, but it may fail in sendMedia
83
84
  }
84
85
  }
85
86
  // Otherwise, use the provided target (either already in correct format or for sendText)
86
- console.log(`[xyOutbound.resolveTarget] Using provided target:`, trimmedTo);
87
+ logger.log(`[xyOutbound.resolveTarget] Using provided target:`, trimmedTo);
87
88
  return {
88
89
  ok: true,
89
90
  to: trimmedTo,
@@ -95,36 +96,36 @@ export const xyOutbound = {
95
96
  // Handle default push marker (for cron jobs without explicit target)
96
97
  let actualTo = to;
97
98
  if (to === DEFAULT_PUSH_MARKER) {
98
- console.log(`[xyOutbound.sendText] Using default push delivery (no specific target)`);
99
+ logger.log(`[xyOutbound.sendText] Using default push delivery (no specific target)`);
99
100
  // For push notifications, we don't need a specific target
100
101
  // The push service will handle it based on config
101
102
  actualTo = config.defaultSessionId || "";
102
103
  }
103
104
  // 1. 持䚅化推送æļˆæ¯å†…厚īŧŒčŽˇå– pushDataId
104
- console.log(`[xyOutbound.sendText] Saving push data to local storage...`);
105
+ logger.log(`[xyOutbound.sendText] Saving push data to local storage...`);
105
106
  let pushDataId;
106
107
  try {
107
108
  pushDataId = await savePushData(text);
108
- console.log(`[xyOutbound.sendText] ✅ Push data saved with ID: ${pushDataId.substring(0, 20)}`);
109
+ logger.log(`[xyOutbound.sendText] ✅ Push data saved with ID: ${pushDataId.substring(0, 20)}`);
109
110
  }
110
111
  catch (error) {
111
- console.error(`[xyOutbound.sendText] ❌ Failed to save push data:`, error);
112
+ logger.error(`[xyOutbound.sendText] ❌ Failed to save push data:`, error);
112
113
  // åĻ‚æžœæŒäš…åŒ–å¤ąč´ĨīŧŒäģį„ļįģ§įģ­å‘送īŧˆä¸é˜ģåĄžä¸ģæĩį¨‹īŧ‰
113
114
  pushDataId = "";
114
115
  }
115
116
  // 2. č¯ģ取所有 pushId
116
- console.log(`[xyOutbound.sendText] Loading all pushIds...`);
117
+ logger.log(`[xyOutbound.sendText] Loading all pushIds...`);
117
118
  let pushIdList = [];
118
119
  try {
119
120
  pushIdList = await getAllPushIds();
120
- console.log(`[xyOutbound.sendText] ✅ Loaded ${pushIdList.length} pushIds`);
121
+ logger.log(`[xyOutbound.sendText] ✅ Loaded ${pushIdList.length} pushIds`);
121
122
  }
122
123
  catch (error) {
123
- console.error(`[xyOutbound.sendText] ❌ Failed to load pushIds:`, error);
124
+ logger.error(`[xyOutbound.sendText] ❌ Failed to load pushIds:`, error);
124
125
  }
125
126
  // 3. åĻ‚æžœ pushIdList ä¸ēįŠēīŧŒå›žé€€åˆ°åŽŸæœ‰é€ģ辑īŧˆäŊŋᔍ config pushIdīŧ‰
126
127
  if (pushIdList.length === 0) {
127
- console.log(`[xyOutbound.sendText] âš ī¸ No pushIds found, falling back to config pushId`);
128
+ logger.log(`[xyOutbound.sendText] âš ī¸ No pushIds found, falling back to config pushId`);
128
129
  pushIdList = [config.pushId];
129
130
  }
130
131
  // Create push service
@@ -134,7 +135,7 @@ export const xyOutbound = {
134
135
  // Truncate push content to max length 1000
135
136
  const pushText = text.length > 1000 ? text.slice(0, 1000) : text;
136
137
  // 4. 遍历所有 pushIdīŧŒäžæŦĄå‘送推送通įŸĨ
137
- console.log(`[xyOutbound.sendText] 📤 Broadcasting to ${pushIdList.length} pushId(s)...`);
138
+ logger.log(`[xyOutbound.sendText] 📤 Broadcasting to ${pushIdList.length} pushId(s)...`);
138
139
  let successCount = 0;
139
140
  let failureCount = 0;
140
141
  for (const pushId of pushIdList) {
@@ -142,11 +143,11 @@ export const xyOutbound = {
142
143
  // äŧ å…Ĩ pushId 和 pushDataIdīŧŒäŊŋᔍ kind="data" æ ŧåŧ
143
144
  await pushService.sendPush(pushText, title, undefined, actualTo, pushDataId, pushId);
144
145
  successCount++;
145
- console.log(`[xyOutbound.sendText] ✅ Sent successfully to pushId: ${pushId.substring(0, 20)}...`);
146
+ logger.log(`[xyOutbound.sendText] ✅ Sent successfully to pushId: ${pushId.substring(0, 20)}...`);
146
147
  }
147
148
  catch (error) {
148
149
  failureCount++;
149
- console.error(`[xyOutbound.sendText] ❌ Failed to send to pushId: ${pushId.substring(0, 20)}...`, error);
150
+ logger.error(`[xyOutbound.sendText] ❌ Failed to send to pushId: ${pushId.substring(0, 20)}...`, error);
150
151
  // 单ä¸Ē pushId å‘é€å¤ąč´Ĩ不åŊąå“å…ļäģ–īŧŒįģ§įģ­å¤„ᐆ䏋䏀ä¸Ē
151
152
  }
152
153
  }
@@ -178,7 +179,7 @@ export const xyOutbound = {
178
179
  if (!fileId) {
179
180
  throw new Error(`File upload returned empty fileId for: ${mediaUrl}`);
180
181
  }
181
- console.log(`[xyOutbound.sendMedia] File uploaded:`, {
182
+ logger.log(`[xyOutbound.sendMedia] File uploaded:`, {
182
183
  fileId,
183
184
  sessionId,
184
185
  taskId,
@@ -222,7 +223,7 @@ export const xyOutbound = {
222
223
  const { getXYWebSocketManager } = await import("./client.js");
223
224
  const wsManager = getXYWebSocketManager(config);
224
225
  await wsManager.sendMessage(sessionId, agentResponse);
225
- console.log(`[xyOutbound.sendMedia] WebSocket message sent successfully`);
226
+ logger.log(`[xyOutbound.sendMedia] WebSocket message sent successfully`);
226
227
  // Return message info
227
228
  return {
228
229
  channel: "xiaoyi-channel",
@@ -127,7 +127,7 @@ function createRetryingStream(createStream, cronJob) {
127
127
  if (!hasContent && !isContent) {
128
128
  // ── Buffer phase (no content yet) ──
129
129
  if (event.type === "done") {
130
- console.log(`[xiaoyiprovider] stream completed (no content), usage: input=${event.message?.usage?.input} output=${event.message?.usage?.output}`);
130
+ logger.log(`[xiaoyiprovider] stream completed (no content), usage: input=${event.message?.usage?.input} output=${event.message?.usage?.output}`);
131
131
  for (const b of buffer)
132
132
  yield b;
133
133
  resultResolve(event.message);
@@ -142,7 +142,7 @@ function createRetryingStream(createStream, cronJob) {
142
142
  else {
143
143
  // ── Streaming phase ──
144
144
  if (!hasContent) {
145
- console.log("[xiaoyiprovider] first content event received, switching to streaming mode");
145
+ logger.log("[xiaoyiprovider] first content event received, switching to streaming mode");
146
146
  hasContent = true;
147
147
  for (const b of buffer)
148
148
  yield b;
@@ -151,13 +151,13 @@ function createRetryingStream(createStream, cronJob) {
151
151
  // The SDK calls result() when it sees done/error — if we yield first, the generator
152
152
  // suspends and can never reach resolve, causing a permanent deadlock.
153
153
  if (event.type === "done") {
154
- console.log(`[xiaoyiprovider] stream completed, usage: input=${event.message?.usage?.input} output=${event.message?.usage?.output}`);
154
+ logger.log(`[xiaoyiprovider] stream completed, usage: input=${event.message?.usage?.input} output=${event.message?.usage?.output}`);
155
155
  resultResolve(event.message);
156
156
  yield event;
157
157
  return;
158
158
  }
159
159
  if (event.type === "error") {
160
- console.log(`[xiaoyiprovider] stream error after content: ${event.error?.errorMessage}`);
160
+ logger.log(`[xiaoyiprovider] stream error after content: ${event.error?.errorMessage}`);
161
161
  errorResult = event.error;
162
162
  break; // break inner loop, proceed to retry decision
163
163
  }
@@ -168,15 +168,15 @@ function createRetryingStream(createStream, cronJob) {
168
168
  if (errorResult?.stopReason === "error" && isRetryableProviderError(errorResult.errorMessage)) {
169
169
  if (attempt < MAX_RETRY_ATTEMPTS - 1) {
170
170
  const delayMs = getRetryDelayMs(attempt + 1, cronJob);
171
- console.log(`[xiaoyiprovider] retryable error (attempt ${attempt + 1}/${MAX_RETRY_ATTEMPTS}): ` +
171
+ logger.log(`[xiaoyiprovider] retryable error (attempt ${attempt + 1}/${MAX_RETRY_ATTEMPTS}): ` +
172
172
  `${errorResult.errorMessage} — retrying in ${delayMs}ms`);
173
173
  await sleep(delayMs);
174
174
  continue; // discard buffer, retry with a new stream
175
175
  }
176
- console.log(`[xiaoyiprovider] all ${MAX_RETRY_ATTEMPTS} retries exhausted, surfacing last error`);
176
+ logger.log(`[xiaoyiprovider] all ${MAX_RETRY_ATTEMPTS} retries exhausted, surfacing last error`);
177
177
  }
178
178
  else if (errorResult) {
179
- console.log(`[xiaoyiprovider] non-retryable error: ${errorResult.errorMessage}`);
179
+ logger.log(`[xiaoyiprovider] non-retryable error: ${errorResult.errorMessage}`);
180
180
  }
181
181
  // Non-retryable or retries exhausted — yield buffered events.
182
182
  // Resolve before yielding the terminal event to avoid the same deadlock.
@@ -196,7 +196,7 @@ function createRetryingStream(createStream, cronJob) {
196
196
  return;
197
197
  }
198
198
  // Safety: final fallback attempt
199
- console.log("[xiaoyiprovider] entering final fallback attempt");
199
+ logger.log("[xiaoyiprovider] entering final fallback attempt");
200
200
  const lastStream = await createStream();
201
201
  for await (const event of lastStream) {
202
202
  if (event.type === "done") {
@@ -485,9 +485,9 @@ export const xiaoyiProvider = {
485
485
  }
486
486
  }
487
487
  // 莰åŊ•čž“å…Ĩ
488
- console.log(`[xiaoyiprovider] input messages count: ${context.messages?.length ?? 0}`);
488
+ logger.log(`[xiaoyiprovider] input messages count: ${context.messages?.length ?? 0}`);
489
489
  if (context.systemPrompt) {
490
- console.log(`[xiaoyiprovider] system prompt length: ${context.systemPrompt.length}`);
490
+ logger.log(`[xiaoyiprovider] system prompt length: ${context.systemPrompt.length}`);
491
491
  }
492
492
  // Reuse deviceType from extraParams instead of calling getCurrentSessionContext()
493
493
  // again (which may be ambiguous in multi-session or async scenarios).
@@ -518,7 +518,7 @@ export const xiaoyiProvider = {
518
518
  sp = sp.replace('## Runtime', combined + '\n\n## Runtime');
519
519
  }
520
520
  }
521
- console.log(`[xiaoyiprovider] system prompt optimized: ${beforeLen} -> ${sp.length}`);
521
+ logger.log(`[xiaoyiprovider] system prompt optimized: ${beforeLen} -> ${sp.length}`);
522
522
  context.systemPrompt = sp;
523
523
  }
524
524
  const selfEvolutionEnabled = await selfEvolutionManager.isEnabled();
@@ -550,7 +550,7 @@ export const xiaoyiProvider = {
550
550
  // ── Retry-capable streaming ──────────────────────────────
551
551
  const cronJob = isCronTriggered(context.messages);
552
552
  if (cronJob)
553
- console.log("[xiaoyiprovider] detected cron-triggered request, using extended retry delays");
553
+ logger.log("[xiaoyiprovider] detected cron-triggered request, using extended retry delays");
554
554
  const makeStream = () => underlying(model, context, {
555
555
  ...options,
556
556
  headers: {
package/dist/src/push.js CHANGED
@@ -1,6 +1,7 @@
1
1
  // Push message service for scheduled tasks
2
2
  import fetch from "node-fetch";
3
3
  import { randomUUID } from "crypto";
4
+ import { logger } from "./utils/logger.js";
4
5
  /**
5
6
  * Service for sending push messages to users.
6
7
  * Used for outbound messages and scheduled tasks.
@@ -33,8 +34,8 @@ export class XYPushService {
33
34
  const traceId = this.generateTraceId();
34
35
  // Use provided pushId or fall back to config pushId
35
36
  const actualPushId = pushId || this.config.pushId;
36
- console.log(`[PUSH] 📤 Preparing to send push message`);
37
- console.log(`[PUSH] - Using pushId: ${actualPushId.substring(0, 20)}...`);
37
+ logger.log(`[PUSH] 📤 Preparing to send push message`);
38
+ logger.log(`[PUSH] - Using pushId: ${actualPushId.substring(0, 20)}...`);
38
39
  try {
39
40
  const requestBody = {
40
41
  jsonrpc: "2.0",
@@ -80,12 +81,12 @@ export class XYPushService {
80
81
  body: JSON.stringify(requestBody),
81
82
  });
82
83
  // Log response status and headers
83
- console.log(`[PUSH] đŸ“Ĩ Response received`);
84
- console.log(`[PUSH] - HTTP Status: ${response.status} ${response.statusText}`);
84
+ logger.log(`[PUSH] đŸ“Ĩ Response received`);
85
+ logger.log(`[PUSH] - HTTP Status: ${response.status} ${response.statusText}`);
85
86
  if (!response.ok) {
86
87
  const errorText = await response.text();
87
- console.log(`[PUSH] ❌ Push request failed`);
88
- console.log(`[PUSH] - HTTP Status: ${response.status}`);
88
+ logger.log(`[PUSH] ❌ Push request failed`);
89
+ logger.log(`[PUSH] - HTTP Status: ${response.status}`);
89
90
  throw new Error(`Push failed: HTTP ${response.status} - ${errorText}`);
90
91
  }
91
92
  // Try to parse JSON response with detailed error handling
@@ -93,7 +94,7 @@ export class XYPushService {
93
94
  try {
94
95
  const responseText = await response.text();
95
96
  if (!responseText || responseText.trim() === '') {
96
- console.log(`[PUSH] âš ī¸ Received empty response body`);
97
+ logger.log(`[PUSH] âš ī¸ Received empty response body`);
97
98
  result = {};
98
99
  }
99
100
  else {
@@ -101,21 +102,21 @@ export class XYPushService {
101
102
  }
102
103
  }
103
104
  catch (parseError) {
104
- console.log(`[PUSH] ❌ Failed to parse JSON response`);
105
- console.log(`[PUSH] - Parse error: ${parseError instanceof Error ? parseError.message : String(parseError)}`);
105
+ logger.log(`[PUSH] ❌ Failed to parse JSON response`);
106
+ logger.log(`[PUSH] - Parse error: ${parseError instanceof Error ? parseError.message : String(parseError)}`);
106
107
  throw new Error(`Invalid JSON response from push service: ${parseError instanceof Error ? parseError.message : String(parseError)}`);
107
108
  }
108
- console.log(`[PUSH] ✅ Push message sent successfully`);
109
- console.log(`[PUSH] - Trace ID: ${traceId}`);
109
+ logger.log(`[PUSH] ✅ Push message sent successfully`);
110
+ logger.log(`[PUSH] - Trace ID: ${traceId}`);
110
111
  }
111
112
  catch (error) {
112
- console.log(`[PUSH] ❌ Failed to send push message`);
113
+ logger.log(`[PUSH] ❌ Failed to send push message`);
113
114
  if (error instanceof Error) {
114
- console.log(`[PUSH] - Error name: ${error.name}`);
115
- console.log(`[PUSH] - Error message: ${error.message}`);
115
+ logger.log(`[PUSH] - Error name: ${error.name}`);
116
+ logger.log(`[PUSH] - Error message: ${error.message}`);
116
117
  }
117
118
  else {
118
- console.log(`[PUSH] - Error:`, error);
119
+ logger.log(`[PUSH] - Error:`, error);
119
120
  }
120
121
  throw error;
121
122
  }
@@ -4,6 +4,7 @@ import { resolveXYConfig } from "./config.js";
4
4
  import { getCurrentTaskId, getCurrentMessageId } from "./task-manager.js";
5
5
  import fs from "fs/promises";
6
6
  import path from "path";
7
+ import { logger } from "./utils/logger.js";
7
8
  const TEMP_FILE_TTL_MS = 24 * 60 * 60 * 1000; // 24 hours
8
9
  /**
9
10
  * æ¸…į† /tmp/xy_channel į›ŽåŊ•中čļ…čŋ‡ 24 小æ—ļįš„æ—§æ–‡äģļ
@@ -31,11 +32,11 @@ export async function cleanupStaleTempFiles(tempDir = "/tmp/xy_channel") {
31
32
  }
32
33
  }
33
34
  if (cleanedCount > 0) {
34
- console.log(`[CLEANUP] 🧹 Cleaned ${cleanedCount} stale files (>${TEMP_FILE_TTL_MS / 1000 / 3600}h) from ${tempDir}`);
35
+ logger.log(`[CLEANUP] 🧹 Cleaned ${cleanedCount} stale files (>${TEMP_FILE_TTL_MS / 1000 / 3600}h) from ${tempDir}`);
35
36
  }
36
37
  }
37
38
  catch (err) {
38
- console.error(`[CLEANUP] ❌ Failed to cleanup temp dir:`, err);
39
+ logger.error(`[CLEANUP] ❌ Failed to cleanup temp dir:`, err);
39
40
  }
40
41
  }
41
42
  /**
@@ -1,4 +1,5 @@
1
1
  import { searchTools, formatToolsForContext, extractUserQuery } from "./tool-search.js";
2
+ import { logger } from "../utils/logger.js";
2
3
  const TOOL_RETRIEVER_HEADER = `[įŗģį쟿¯īŧŒéžį”¨æˆˇå‘言]
3
4
 
4
5
  `;
@@ -63,10 +64,10 @@ export function createBeforePromptBuildHandler(config) {
63
64
  if (!searchResult || searchResult.tools.length === 0) {
64
65
  return undefined;
65
66
  }
66
- console.log(`${PLUGIN_LOG_PREFIX} [RESULT] Found ${searchResult.tools.length} skills, building context...`);
67
+ logger.log(`${PLUGIN_LOG_PREFIX} [RESULT] Found ${searchResult.tools.length} skills, building context...`);
67
68
  const toolsContext = formatToolsForContext(searchResult, config.includeUninstalledOnly);
68
69
  if (!toolsContext) {
69
- console.log(`${PLUGIN_LOG_PREFIX} [ERROR] Failed to format skills context`);
70
+ logger.log(`${PLUGIN_LOG_PREFIX} [ERROR] Failed to format skills context`);
70
71
  return undefined;
71
72
  }
72
73
  return {
@@ -75,7 +76,7 @@ export function createBeforePromptBuildHandler(config) {
75
76
  }
76
77
  catch (error) {
77
78
  const errorMessage = error instanceof Error ? error.message : String(error);
78
- console.error(`${PLUGIN_LOG_PREFIX} [ERROR] ${errorMessage}, original query: "${extractedQuery}"`);
79
+ logger.error(`${PLUGIN_LOG_PREFIX} [ERROR] ${errorMessage}, original query: "${extractedQuery}"`);
79
80
  return undefined;
80
81
  }
81
82
  };
@@ -1,6 +1,7 @@
1
1
  import * as fs from "fs";
2
2
  import * as path from "path";
3
3
  import * as os from "os";
4
+ import { logger } from "../utils/logger.js";
4
5
  const SKILL_ID = "celia_find_skills";
5
6
  const PLUGIN_LOG_PREFIX = "[skill-retriever]";
6
7
  export function extractUserQuery(fullPrompt) {
@@ -89,7 +90,7 @@ export async function searchTools(options) {
89
90
  const apiKey = configApiKey ?? envConfig.PERSONAL_API_KEY;
90
91
  const uid = configUid ?? envConfig.PERSONAL_UID;
91
92
  if (!serviceUrl || !apiKey || !uid) {
92
- console.warn(`${PLUGIN_LOG_PREFIX} Missing required configuration. serviceUrl: "${serviceUrl}", apiKey: "${apiKey ? '(set)' : '(missing)'} ", uid: "${uid ? '(set)' : '(missing)'}"`);
93
+ logger.warn(`${PLUGIN_LOG_PREFIX} Missing required configuration. serviceUrl: "${serviceUrl}", apiKey: "${apiKey ? '(set)' : '(missing)'} ", uid: "${uid ? '(set)' : '(missing)'}"`);
93
94
  return null;
94
95
  }
95
96
  const traceId = crypto.randomUUID();
@@ -111,10 +112,10 @@ export async function searchTools(options) {
111
112
  signal: AbortSignal.timeout(timeoutMs),
112
113
  });
113
114
  if (!response.ok) {
114
- console.warn(`${PLUGIN_LOG_PREFIX} HTTP error: ${response.status} ${response.statusText}`);
115
+ logger.warn(`${PLUGIN_LOG_PREFIX} HTTP error: ${response.status} ${response.statusText}`);
115
116
  return null;
116
117
  }
117
- console.log(`${PLUGIN_LOG_PREFIX} Received response, status: ${response.status}`);
118
+ logger.log(`${PLUGIN_LOG_PREFIX} Received response, status: ${response.status}`);
118
119
  const responseData = await response.json();
119
120
  if (responseData.errorCode === "0" &&
120
121
  responseData.content &&
@@ -125,18 +126,18 @@ export async function searchTools(options) {
125
126
  const topTools = formattedData.slice(0, 2);
126
127
  const allInstalled = topTools.every((tool) => tool.status === "åˇ˛åŽ‰čŖ…");
127
128
  if (allInstalled) {
128
- console.log(`${PLUGIN_LOG_PREFIX} [DEBUG] All top 2 skills are installed, returning null`);
129
+ logger.log(`${PLUGIN_LOG_PREFIX} [DEBUG] All top 2 skills are installed, returning null`);
129
130
  return null;
130
131
  }
131
132
  const hasInstalledWithHighScore = topTools.some((tool) => tool.status === "åˇ˛åŽ‰čŖ…" && (tool.rrfScore ?? 0) >= 0.016);
132
133
  if (hasInstalledWithHighScore) {
133
- console.log(`${PLUGIN_LOG_PREFIX} [DEBUG] Top 2 has installed skill with rrfScore >= 0.016, returning null`);
134
+ logger.log(`${PLUGIN_LOG_PREFIX} [DEBUG] Top 2 has installed skill with rrfScore >= 0.016, returning null`);
134
135
  return null;
135
136
  }
136
137
  let filteredTools = topTools.filter((tool) => tool.status === "æœĒåŽ‰čŖ…" && (tool.rrfScore ?? 0) >= 0.016);
137
- console.log(`${PLUGIN_LOG_PREFIX} [DEBUG] After filtering uninstalled with rrfScore >= 0.016: ${filteredTools.length}, details: ${filteredTools.map((t) => `${t.skillId}(rrfScore=${t.rrfScore})`).join(", ")}`);
138
+ logger.log(`${PLUGIN_LOG_PREFIX} [DEBUG] After filtering uninstalled with rrfScore >= 0.016: ${filteredTools.length}, details: ${filteredTools.map((t) => `${t.skillId}(rrfScore=${t.rrfScore})`).join(", ")}`);
138
139
  if (filteredTools.length === 0) {
139
- console.log(`${PLUGIN_LOG_PREFIX} [DEBUG] No uninstalled skills with rrfScore >= 0.016, returning null`);
140
+ logger.log(`${PLUGIN_LOG_PREFIX} [DEBUG] No uninstalled skills with rrfScore >= 0.016, returning null`);
140
141
  return null;
141
142
  }
142
143
  return {
@@ -145,7 +146,7 @@ export async function searchTools(options) {
145
146
  timestamp: Date.now(),
146
147
  };
147
148
  }
148
- console.warn(`${PLUGIN_LOG_PREFIX} Invalid response format: ${JSON.stringify(responseData).slice(0, 200)}`);
149
+ logger.warn(`${PLUGIN_LOG_PREFIX} Invalid response format: ${JSON.stringify(responseData).slice(0, 200)}`);
149
150
  return null;
150
151
  }
151
152
  catch (error) {
@@ -153,7 +154,7 @@ export async function searchTools(options) {
153
154
  const errorMessage = error instanceof Error ? error.message : String(error);
154
155
  const errorCause = error instanceof Error && error.cause ? JSON.stringify(error.cause) : "N/A";
155
156
  const errorStack = error instanceof Error ? error.stack?.split("\n").slice(0, 3).join(" | ") : "N/A";
156
- console.warn(`${PLUGIN_LOG_PREFIX} [ERROR] Fetch failed - name: ${errorName}, message: ${errorMessage}, cause: ${errorCause}, stack: ${errorStack}`);
157
+ logger.warn(`${PLUGIN_LOG_PREFIX} [ERROR] Fetch failed - name: ${errorName}, message: ${errorMessage}, cause: ${errorCause}, stack: ${errorStack}`);
157
158
  return null;
158
159
  }
159
160
  }
@@ -57,7 +57,7 @@ export async function tryInjectSteer(sessionKey, message) {
57
57
  },
58
58
  },
59
59
  };
60
- console.log(`[STEER] Injecting steer for sessionId=${sessionId}, taskId=${syntheticMessage.params.id}`);
60
+ logger.log(`[STEER] Injecting steer for sessionId=${sessionId}, taskId=${syntheticMessage.params.id}`);
61
61
  try {
62
62
  await handleXYMessage({
63
63
  cfg: cachedCfg,
@@ -1,5 +1,6 @@
1
1
  import { getXYWebSocketManager } from "../client.js";
2
2
  import { XYFileUploadService } from "../file-upload.js";
3
+ import { logger } from "../utils/logger.js";
3
4
  import fetch from "node-fetch";
4
5
  import fs from "fs/promises";
5
6
  import path from "path";
@@ -97,7 +98,7 @@ async function downloadRemoteFile(url) {
97
98
  */
98
99
  export function createSendFileToUserTool(ctx) {
99
100
  const { config, sessionId, taskId, messageId } = ctx;
100
- console.log(`[SEND-FILE-TO-USER] 🏭 CREATE: sessionId=${sessionId} taskId=${taskId}`);
101
+ logger.log(`[SEND-FILE-TO-USER] 🏭 CREATE: sessionId=${sessionId} taskId=${taskId}`);
101
102
  return {
102
103
  name: "send_file_to_user",
103
104
  label: "Send File to User",
@@ -236,10 +237,10 @@ b. 操äŊœčļ…æ—ļæ—ļ间ä¸ē2分钟īŧˆ120į§’īŧ‰īŧŒč¯ˇå‹ŋé‡å¤č°ƒį”¨æ­¤åˇĨå…ˇīŧŒåĻ‚
236
237
  error: { code: 0 },
237
238
  }),
238
239
  };
239
- console.log(`[SEND-FILE-TO-USER] 🚀 EXEC sending: sessionId=${sessionId} taskId=${taskId} fileName=${fileName}`);
240
+ logger.log(`[SEND-FILE-TO-USER] 🚀 EXEC sending: sessionId=${sessionId} taskId=${taskId} fileName=${fileName}`);
240
241
  // Send WebSocket message
241
242
  await wsManager.sendMessage(sessionId, agentResponse);
242
- console.log(`send ${fileName} file to user success`);
243
+ logger.log(`send ${fileName} file to user success`);
243
244
  sentFiles.push({ fileName, fileId });
244
245
  }
245
246
  return {
@@ -103,7 +103,7 @@ export function getLatestSessionContext() {
103
103
  * This ensures thread-safe context isolation for concurrent requests.
104
104
  */
105
105
  export function runWithSessionContext(context, callback) {
106
- console.log(`[SESSION-MGR] đŸ”ĩ ALS SET: sessionId=${context.sessionId} taskId=${context.taskId}`);
106
+ logger.log(`[SESSION-MGR] đŸ”ĩ ALS SET: sessionId=${context.sessionId} taskId=${context.taskId}`);
107
107
  return asyncLocalStorage.run(context, callback);
108
108
  }
109
109
  /**
@@ -124,7 +124,7 @@ export function getCurrentSessionContext(sessionKey) {
124
124
  }
125
125
  // ALS not available — logging to understand when/why
126
126
  const stack = new Error().stack?.split("\n").slice(2, 5).map(s => s.trim()).join(" | ");
127
- console.log(`[SESSION-MGR] âš ī¸ ALS miss, falling back to Map (size=${activeSessions.size}), callers: ${stack}`);
127
+ logger.log(`[SESSION-MGR] âš ī¸ ALS miss, falling back to Map (size=${activeSessions.size}), callers: ${stack}`);
128
128
  // 2. Fallback: look up from global activeSessions Map
129
129
  if (activeSessions.size === 0) {
130
130
  return null;
@@ -165,7 +165,7 @@ export function getCurrentSessionContext(sessionKey) {
165
165
  if (lastKey) {
166
166
  const lastEntry = activeSessions.get(lastKey);
167
167
  if (lastEntry) {
168
- console.log(`[SESSION-MGR] đŸŽ¯ using lastRegistered session: ${lastKey}`);
168
+ logger.log(`[SESSION-MGR] đŸŽ¯ using lastRegistered session: ${lastKey}`);
169
169
  const { refCount, createdAt, ...context } = lastEntry;
170
170
  return enrichWithLatestTaskInfo(context);
171
171
  }
@@ -1,5 +1,6 @@
1
1
  import fs from "node:fs";
2
2
  import fsp from "node:fs/promises";
3
+ import { logger } from "./logger.js";
3
4
  const SELF_EVOLUTION_ENV_FILE = "/home/sandbox/.openclaw/.xiaoyiruntime";
4
5
  const SELF_EVOLUTION_ENV_KEY = "selfEvolutionState";
5
6
  function parseBooleanLike(value) {
@@ -24,7 +25,7 @@ class SelfEvolutionManager {
24
25
  catch (error) {
25
26
  const code = error && typeof error === "object" && "code" in error ? error.code : undefined;
26
27
  if (code !== "ENOENT") {
27
- console.error(`[SELF_EVOLUTION] Failed to read ${SELF_EVOLUTION_ENV_FILE}:`, error);
28
+ logger.error(`[SELF_EVOLUTION] Failed to read ${SELF_EVOLUTION_ENV_FILE}:`, error);
28
29
  }
29
30
  return false;
30
31
  }
@@ -59,7 +60,7 @@ class SelfEvolutionManager {
59
60
  catch (error) {
60
61
  const code = error && typeof error === "object" && "code" in error ? error.code : undefined;
61
62
  if (code !== "ENOENT") {
62
- console.error(`[SELF_EVOLUTION] Failed to read ${SELF_EVOLUTION_ENV_FILE}:`, error);
63
+ logger.error(`[SELF_EVOLUTION] Failed to read ${SELF_EVOLUTION_ENV_FILE}:`, error);
63
64
  }
64
65
  return false;
65
66
  }
@@ -1,4 +1,5 @@
1
1
  // WebSocket connection manager (Single connection)
2
+ import os from "os";
2
3
  import WebSocket from "ws";
3
4
  import { EventEmitter } from "events";
4
5
  import { HeartbeatManager } from "./heartbeat.js";
@@ -293,10 +294,11 @@ export class XYWebSocketManager extends EventEmitter {
293
294
  this.error("Cannot send init message: WebSocket not open");
294
295
  return;
295
296
  }
297
+ const hostname = os.hostname();
296
298
  const initMessage = {
297
299
  msgType: "clawd_bot_init",
298
300
  agentId: this.config.agentId,
299
- msgDetail: JSON.stringify({ agentId: this.config.agentId }),
301
+ msgDetail: JSON.stringify({ agentId: this.config.agentId, hostname }),
300
302
  };
301
303
  const initMessageStr = JSON.stringify(initMessage);
302
304
  this.log("[WS-SEND] Sending init message frame:", JSON.stringify(initMessage, null, 2));
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ynhcj/xiaoyi-channel",
3
- "version": "0.0.125-beta",
3
+ "version": "0.0.126-beta",
4
4
  "description": "OpenClaw Xiaoyi Channel plugin - Xiaoyi A2A protocol integration",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",