@ynhcj/xiaoyi-channel 1.1.26 → 1.1.28

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.
Files changed (104) hide show
  1. package/dist/index.js +26 -69
  2. package/dist/src/approval-bridge.d.ts +48 -0
  3. package/dist/src/approval-bridge.js +382 -0
  4. package/dist/src/bot.js +132 -73
  5. package/dist/src/channel.js +59 -5
  6. package/dist/src/client.js +13 -23
  7. package/dist/src/cron-command.d.ts +15 -0
  8. package/dist/src/cron-command.js +49 -0
  9. package/dist/src/cron-query-handler.d.ts +7 -0
  10. package/dist/src/cron-query-handler.js +189 -0
  11. package/dist/src/cspl/call_api.d.ts +2 -0
  12. package/dist/src/cspl/call_api.js +107 -0
  13. package/dist/src/cspl/config.d.ts +4 -17
  14. package/dist/src/cspl/config.js +100 -70
  15. package/dist/src/cspl/configs.json +10 -0
  16. package/dist/src/cspl/constants.d.ts +49 -24
  17. package/dist/src/cspl/constants.js +46 -16
  18. package/dist/src/cspl/sentinel_hook.d.ts +2 -0
  19. package/dist/src/cspl/sentinel_hook.js +103 -0
  20. package/dist/src/cspl/steer-context.js +1 -1
  21. package/dist/src/cspl/upload_file.d.ts +1 -0
  22. package/dist/src/cspl/upload_file.js +211 -0
  23. package/dist/src/cspl/utils.d.ts +17 -2
  24. package/dist/src/cspl/utils.js +271 -15
  25. package/dist/src/file-upload.d.ts +5 -0
  26. package/dist/src/file-upload.js +102 -0
  27. package/dist/src/formatter.d.ts +43 -1
  28. package/dist/src/formatter.js +171 -41
  29. package/dist/src/monitor.js +64 -43
  30. package/dist/src/outbound.js +8 -9
  31. package/dist/src/parser.d.ts +8 -1
  32. package/dist/src/parser.js +71 -0
  33. package/dist/src/provider.js +51 -17
  34. package/dist/src/push.d.ts +11 -1
  35. package/dist/src/push.js +101 -17
  36. package/dist/src/reply-dispatcher.js +152 -59
  37. package/dist/src/self-evolution-handler.d.ts +1 -1
  38. package/dist/src/self-evolution-handler.js +14 -3
  39. package/dist/src/sensitive-redactor.d.ts +4 -0
  40. package/dist/src/sensitive-redactor.js +364 -0
  41. package/dist/src/task-manager.js +6 -10
  42. package/dist/src/tools/agent-as-skill-tool.d.ts +7 -0
  43. package/dist/src/tools/agent-as-skill-tool.js +190 -0
  44. package/dist/src/tools/calendar-tool.js +3 -2
  45. package/dist/src/tools/call-phone-tool.js +3 -2
  46. package/dist/src/tools/check-plugin-privilege-tool.d.ts +6 -0
  47. package/dist/src/tools/check-plugin-privilege-tool.js +182 -0
  48. package/dist/src/tools/create-alarm-tool.js +3 -2
  49. package/dist/src/tools/create-all-tools.js +11 -3
  50. package/dist/src/tools/delete-alarm-tool.js +3 -2
  51. package/dist/src/tools/device-tool-map.d.ts +1 -1
  52. package/dist/src/tools/device-tool-map.js +12 -5
  53. package/dist/src/tools/discover-cross-devices-tool.d.ts +2 -0
  54. package/dist/src/tools/discover-cross-devices-tool.js +235 -0
  55. package/dist/src/tools/display-a2ui-card-tool.d.ts +2 -0
  56. package/dist/src/tools/display-a2ui-card-tool.js +85 -0
  57. package/dist/src/tools/find-pc-devices-tool.d.ts +2 -1
  58. package/dist/src/tools/find-pc-devices-tool.js +85 -88
  59. package/dist/src/tools/get-collection-tool-schema.js +1 -1
  60. package/dist/src/tools/location-tool.js +3 -2
  61. package/dist/src/tools/modify-alarm-tool.js +3 -2
  62. package/dist/src/tools/modify-note-tool.js +3 -2
  63. package/dist/src/tools/note-tool.js +3 -2
  64. package/dist/src/tools/query-app-message-tool.js +4 -3
  65. package/dist/src/tools/query-memory-data-tool.js +4 -3
  66. package/dist/src/tools/query-todo-task-tool.js +4 -3
  67. package/dist/src/tools/save-file-to-phone-tool.js +3 -2
  68. package/dist/src/tools/save-media-to-gallery-tool.js +3 -2
  69. package/dist/src/tools/schema-tool-factory.js +1 -1
  70. package/dist/src/tools/search-alarm-tool.js +3 -2
  71. package/dist/src/tools/search-calendar-tool.js +3 -2
  72. package/dist/src/tools/search-contact-tool.js +3 -2
  73. package/dist/src/tools/search-email-tool.js +4 -3
  74. package/dist/src/tools/search-file-tool.js +8 -9
  75. package/dist/src/tools/search-message-tool.js +2 -1
  76. package/dist/src/tools/search-note-tool.js +3 -2
  77. package/dist/src/tools/search-photo-gallery-tool.js +5 -4
  78. package/dist/src/tools/send-cross-device-task-tool.d.ts +2 -0
  79. package/dist/src/tools/send-cross-device-task-tool.js +299 -0
  80. package/dist/src/tools/send-email-tool.js +4 -3
  81. package/dist/src/tools/send-file-to-user-tool.d.ts +1 -1
  82. package/dist/src/tools/send-file-to-user-tool.js +37 -8
  83. package/dist/src/tools/send-html-card-tool.d.ts +7 -0
  84. package/dist/src/tools/send-html-card-tool.js +113 -0
  85. package/dist/src/tools/send-message-tool.js +2 -1
  86. package/dist/src/tools/session-manager.d.ts +17 -1
  87. package/dist/src/tools/session-manager.js +87 -1
  88. package/dist/src/tools/upload-file-tool.js +9 -7
  89. package/dist/src/tools/upload-photo-tool.js +5 -4
  90. package/dist/src/tools/xiaoyi-add-collection-tool.js +5 -3
  91. package/dist/src/tools/xiaoyi-collection-tool.js +4 -3
  92. package/dist/src/tools/xiaoyi-delete-collection-tool.js +4 -3
  93. package/dist/src/tools/xiaoyi-gui-tool.js +8 -2
  94. package/dist/src/trigger-handler.js +4 -7
  95. package/dist/src/types.d.ts +25 -1
  96. package/dist/src/utils/config-manager.js +3 -6
  97. package/dist/src/utils/logger.d.ts +8 -0
  98. package/dist/src/utils/logger.js +69 -34
  99. package/dist/src/utils/pushdata-manager.js +1 -5
  100. package/dist/src/utils/pushid-manager.js +1 -2
  101. package/dist/src/utils/runtime-manager.js +1 -4
  102. package/dist/src/websocket.d.ts +3 -0
  103. package/dist/src/websocket.js +242 -38
  104. package/package.json +1 -1
@@ -24,10 +24,10 @@ export function getXYWebSocketManager(config, runtime) {
24
24
  return cached;
25
25
  }
26
26
  // Create new manager
27
- logger.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}`);
28
28
  cached = new XYWebSocketManager(config, runtime);
29
29
  wsManagerCache.set(cacheKey, cached);
30
- logger.log(`[WS-MANAGER-CACHE] 📊 Total managers after creation: ${wsManagerCache.size}`);
30
+ logger.log(`[WS-MANAGER-CACHE] Total managers after creation: ${wsManagerCache.size}`);
31
31
  return cached;
32
32
  }
33
33
  /**
@@ -38,13 +38,13 @@ export function removeXYWebSocketManager(config) {
38
38
  const cacheKey = `${config.apiKey}-${config.agentId}`;
39
39
  const manager = wsManagerCache.get(cacheKey);
40
40
  if (manager) {
41
- logger.log(`🗑️ [WS-MANAGER-CACHE] Removing manager from cache: ${cacheKey}`);
41
+ logger.log(`[WS-MANAGER-CACHE] Removing manager from cache: ${cacheKey}`);
42
42
  manager.disconnect();
43
43
  wsManagerCache.delete(cacheKey);
44
- logger.log(`🗑️ [WS-MANAGER-CACHE] Manager removed, remaining managers: ${wsManagerCache.size}`);
44
+ logger.log(`[WS-MANAGER-CACHE] Manager removed, remaining managers: ${wsManagerCache.size}`);
45
45
  }
46
46
  else {
47
- logger.log(`⚠️ [WS-MANAGER-CACHE] Manager not found in cache: ${cacheKey}`);
47
+ logger.log(`[WS-MANAGER-CACHE] Manager not found in cache: ${cacheKey}`);
48
48
  }
49
49
  }
50
50
  /**
@@ -68,35 +68,25 @@ export function getCachedManagerCount() {
68
68
  * Helps identify connection issues and orphan connections.
69
69
  */
70
70
  export function diagnoseAllManagers() {
71
- logger.log(`Total cached managers: ${wsManagerCache.size}`);
71
+ logger.log(`[DIAG] Total cached managers: ${wsManagerCache.size}`);
72
72
  if (wsManagerCache.size === 0) {
73
- logger.log("ℹ️ No managers in cache");
73
+ logger.log("[DIAG] No managers in cache");
74
74
  return;
75
75
  }
76
76
  let orphanCount = 0;
77
77
  wsManagerCache.forEach((manager, key) => {
78
78
  const diag = manager.getConnectionDiagnostics();
79
- logger.log(` Total event listeners on manager: ${diag.totalEventListeners}`);
80
- // Connection
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}`);
79
+ logger.log(`[DIAG] Manager ${key} — event listeners: ${diag.totalEventListeners} | Connection: exists=${diag.connection.exists}, readyState=${diag.connection.readyState}, stateConnected=${diag.connection.stateConnected}/${diag.connection.stateReady}, reconnectAttempts=${diag.connection.reconnectAttempts}, wsListeners=${diag.connection.listenerCount}, heartbeatActive=${diag.connection.heartbeatActive}, hasReconnectTimer=${diag.connection.hasReconnectTimer}`);
89
80
  if (diag.connection.isOrphan) {
90
- logger.log(` ⚠️ ORPHAN CONNECTION DETECTED!`);
81
+ logger.log(`[DIAG] ORPHAN CONNECTION DETECTED on manager: ${key}`);
91
82
  orphanCount++;
92
83
  }
93
84
  });
94
85
  if (orphanCount > 0) {
95
- logger.log(`⚠️ Total orphan connections found: ${orphanCount}`);
96
- logger.log(`💡 Suggestion: These connections should be cleaned up`);
86
+ logger.log(`[DIAG] Total orphan connections found: ${orphanCount} — these connections should be cleaned up`);
97
87
  }
98
88
  else {
99
- logger.log(`✅ No orphan connections found`);
89
+ logger.log("[DIAG] No orphan connections found");
100
90
  }
101
91
  }
102
92
  /**
@@ -108,13 +98,13 @@ export function cleanupOrphanConnections() {
108
98
  wsManagerCache.forEach((manager, key) => {
109
99
  const diag = manager.getConnectionDiagnostics();
110
100
  if (diag.connection.isOrphan) {
111
- logger.log(`🧹 Cleaning up orphan connections in manager: ${key}`);
101
+ logger.log(`[CLEANUP] Cleaning up orphan connections in manager: ${key}`);
112
102
  manager.disconnect();
113
103
  cleanedCount++;
114
104
  }
115
105
  });
116
106
  if (cleanedCount > 0) {
117
- logger.log(`🧹 Cleaned up ${cleanedCount} manager(s) with orphan connections`);
107
+ logger.log(`[CLEANUP] Cleaned up ${cleanedCount} manager(s) with orphan connections`);
118
108
  }
119
109
  return cleanedCount;
120
110
  }
@@ -0,0 +1,15 @@
1
+ import type { XYChannelConfig, A2ACommand } from "./types.js";
2
+ export interface SendCommandViaPushParams {
3
+ config: XYChannelConfig;
4
+ command: A2ACommand;
5
+ }
6
+ /**
7
+ * Send a tool command through the push channel (for cron-triggered tool calls).
8
+ *
9
+ * Flow:
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
14
+ */
15
+ export declare function sendCommandViaPush(params: SendCommandViaPushParams): Promise<void>;
@@ -0,0 +1,49 @@
1
+ // Cron-triggered tool command delivery via push channel.
2
+ // When a cron/scheduled task executes a tool, there is no active WebSocket
3
+ // session to carry the command. Instead, the command is delivered through
4
+ // the push notification channel (agent-webhook), which reaches the device
5
+ // independently of any session. The device processes the command and returns
6
+ // results through the normal WebSocket connection, so response listening
7
+ // works the same as for regular tool calls.
8
+ import { randomUUID } from "crypto";
9
+ import { XYPushService } from "./push.js";
10
+ import { getAllPushIds } from "./utils/pushid-manager.js";
11
+ import { logger } from "./utils/logger.js";
12
+ /**
13
+ * Send a tool command through the push channel (for cron-triggered tool calls).
14
+ *
15
+ * Flow:
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
20
+ */
21
+ export async function sendCommandViaPush(params) {
22
+ const { config, command } = params;
23
+ const intentName = command.payload?.executeParam?.intentName ??
24
+ command.header?.name ??
25
+ "Command";
26
+ logger.log(`[CRON-CMD] Sending command via push, intent=${intentName}`);
27
+ // 1. Load push IDs, use first one
28
+ let pushId = config.pushId;
29
+ try {
30
+ const pushIdList = await getAllPushIds();
31
+ if (pushIdList.length > 0) {
32
+ pushId = pushIdList[0];
33
+ }
34
+ }
35
+ catch (error) {
36
+ logger.error("[CRON-CMD] Failed to load pushIds:", error);
37
+ }
38
+ // 2. Build and send push notification with command in directives
39
+ const pushService = new XYPushService(config);
40
+ const sessionId = randomUUID();
41
+ try {
42
+ await pushService.sendPushWithDirectives(pushId, sessionId, [command]);
43
+ logger.log(`[CRON-CMD] Push sent successfully, intent=${intentName}`);
44
+ }
45
+ catch (error) {
46
+ logger.error(`[CRON-CMD] Failed to send push`, error);
47
+ throw error;
48
+ }
49
+ }
@@ -0,0 +1,7 @@
1
+ /**
2
+ * Handle a cron-query-event.
3
+ *
4
+ * Calls the Gateway cron RPC and sends the result back through sendCommand
5
+ * as a System.CronQuery command with the full result object in payload.ans.
6
+ */
7
+ export declare function handleCronQueryEvent(context: any, cfg: any): Promise<void>;
@@ -0,0 +1,189 @@
1
+ // Cron query event handler.
2
+ // Listens for cron-query-event from the WebSocket manager,
3
+ // calls Gateway cron RPC via callGatewayTool, and sends the
4
+ // result back to the client via sendCommand as a System.CronQuery
5
+ // command with the result in payload.ans.
6
+ import { callGatewayTool } from "openclaw/plugin-sdk/agent-harness-runtime";
7
+ import * as os from "os";
8
+ import { sendCommand } from "./formatter.js";
9
+ import { resolveXYConfig } from "./config.js";
10
+ import { logger } from "./utils/logger.js";
11
+ import { readFileSync, readdirSync } from "fs";
12
+ import { join } from "path";
13
+ const GATEWAY_TIMEOUT_MS = 60_000;
14
+ /**
15
+ * Handle a cron-query-event.
16
+ *
17
+ * Calls the Gateway cron RPC and sends the result back through sendCommand
18
+ * as a System.CronQuery command with the full result object in payload.ans.
19
+ */
20
+ export async function handleCronQueryEvent(context, cfg) {
21
+ const { action, jobId, params, sessionId, taskId, messageId } = context;
22
+ const log = logger.withContext(sessionId ?? "", taskId ?? "");
23
+ log.log(`[CRON-QUERY] Received event: action=${action}, jobId=${jobId ?? "(none)"}`);
24
+ let result;
25
+ let error;
26
+ try {
27
+ switch (action) {
28
+ case "list":
29
+ result = await callGatewayTool("cron.list", { timeoutMs: GATEWAY_TIMEOUT_MS }, params ?? {});
30
+ break;
31
+ case "status":
32
+ result = await callGatewayTool("cron.status", { timeoutMs: GATEWAY_TIMEOUT_MS }, {});
33
+ break;
34
+ case "runs":
35
+ result = await callGatewayTool("cron.runs", { timeoutMs: GATEWAY_TIMEOUT_MS }, {
36
+ jobId,
37
+ ...params,
38
+ });
39
+ break;
40
+ case "add":
41
+ result = await callGatewayTool("cron.add", { timeoutMs: GATEWAY_TIMEOUT_MS }, params ?? {});
42
+ break;
43
+ case "update":
44
+ result = await callGatewayTool("cron.update", { timeoutMs: GATEWAY_TIMEOUT_MS }, {
45
+ jobId,
46
+ ...params,
47
+ });
48
+ break;
49
+ case "remove":
50
+ result = await callGatewayTool("cron.remove", { timeoutMs: GATEWAY_TIMEOUT_MS }, {
51
+ jobId,
52
+ });
53
+ break;
54
+ case "run":
55
+ result = await callGatewayTool("cron.run", { timeoutMs: GATEWAY_TIMEOUT_MS }, {
56
+ jobId,
57
+ mode: "force",
58
+ ...params,
59
+ });
60
+ break;
61
+ case "queryTimeList":
62
+ result = await queryTimeListLocal();
63
+ break;
64
+ default:
65
+ error = `Unknown action: ${context.action}`;
66
+ log.error(`[CRON-QUERY] ${error}`);
67
+ result = { error };
68
+ }
69
+ }
70
+ catch (err) {
71
+ error = err instanceof Error ? err.message : String(err);
72
+ log.error(`[CRON-QUERY] RPC call failed for action=${action}:`, err);
73
+ result = { error };
74
+ }
75
+ // Log the result
76
+ log.log(`[CRON-QUERY] RPC result for action=${action}: ${JSON.stringify(result, null, 2)}`);
77
+ // Send result back via sendCommand as System.CronQuery with payload.ans
78
+ if (cfg && sessionId && taskId && messageId) {
79
+ try {
80
+ const config = resolveXYConfig(cfg);
81
+ const command = {
82
+ header: {
83
+ namespace: "AgentEvent",
84
+ name: "CronQuery",
85
+ },
86
+ payload: {
87
+ action,
88
+ ans: result,
89
+ },
90
+ };
91
+ await sendCommand({
92
+ config,
93
+ sessionId,
94
+ taskId,
95
+ messageId,
96
+ command,
97
+ final: sessionId.toLowerCase().endsWith("cronquery"),
98
+ });
99
+ log.log(`[CRON-QUERY] Sent response via sendCommand, action=${action}`);
100
+ }
101
+ catch (sendErr) {
102
+ log.error(`[CRON-QUERY] Failed to send response via sendCommand:`, sendErr);
103
+ }
104
+ }
105
+ else {
106
+ log.warn(`[CRON-QUERY] Missing cfg/sessionId/taskId/messageId, skipping sendCommand`);
107
+ }
108
+ }
109
+ /**
110
+ * Read local cron folder directly (bypassing openclaw RPC) and return
111
+ * run records from the last 7 days, grouped by date and sorted by time.
112
+ *
113
+ * Data sources:
114
+ * - state/cron/jobs.json → job id → name mapping
115
+ * - state/cron/runs/*.jsonl → run records (one JSON per line)
116
+ *
117
+ * Return format:
118
+ * [ { "YYYY-MM-DD": [ { run record with .name }, ... ] }, ... ]
119
+ */
120
+ async function queryTimeListLocal() {
121
+ const cronDir = join(os.homedir(), ".openclaw", "cron");
122
+ const jobsPath = join(cronDir, "jobs.json");
123
+ const runsDir = join(cronDir, "runs");
124
+ // 1. Build jobId → name map from jobs.json
125
+ const jobNameMap = {};
126
+ try {
127
+ const jobsRaw = readFileSync(jobsPath, "utf-8");
128
+ const jobsData = JSON.parse(jobsRaw);
129
+ for (const job of jobsData.jobs || []) {
130
+ jobNameMap[job.id] = job.name || job.id;
131
+ }
132
+ }
133
+ catch (err) {
134
+ logger.error(`[CRON-QUERY] Failed to read jobs.json: ${err.message}`);
135
+ }
136
+ // 2. Read all run files, collect runs within last 7 days
137
+ const sevenDaysAgo = Date.now() - 7 * 24 * 60 * 60 * 1000;
138
+ const allRuns = [];
139
+ let files = [];
140
+ try {
141
+ files = readdirSync(runsDir);
142
+ }
143
+ catch {
144
+ files = [];
145
+ }
146
+ for (const file of files) {
147
+ if (!file.endsWith(".jsonl"))
148
+ continue;
149
+ try {
150
+ const content = readFileSync(join(runsDir, file), "utf-8");
151
+ const lines = content.trim().split("\n");
152
+ for (const line of lines) {
153
+ if (!line.trim())
154
+ continue;
155
+ try {
156
+ const run = JSON.parse(line);
157
+ if (run.ts && run.ts >= sevenDaysAgo) {
158
+ run.name = jobNameMap[run.jobId] || run.jobId || "";
159
+ allRuns.push(run);
160
+ }
161
+ }
162
+ catch {
163
+ // skip malformed line
164
+ }
165
+ }
166
+ }
167
+ catch (err) {
168
+ logger.error(`[CRON-QUERY] Failed to read run file ${file}: ${err.message}`);
169
+ }
170
+ }
171
+ // 3. Sort by ts ascending
172
+ allRuns.sort((a, b) => a.ts - b.ts);
173
+ // 4. Group by date (YYYY-MM-DD in local time)
174
+ const grouped = new Map();
175
+ for (const run of allRuns) {
176
+ const d = new Date(run.ts);
177
+ const label = `${d.getFullYear()}-${String(d.getMonth() + 1).padStart(2, "0")}-${String(d.getDate()).padStart(2, "0")}`;
178
+ if (!grouped.has(label)) {
179
+ grouped.set(label, []);
180
+ }
181
+ grouped.get(label).push(run);
182
+ }
183
+ // 5. Convert to ordered array of single-key objects
184
+ const result = [];
185
+ for (const [date, runs] of grouped) {
186
+ result.push({ [date]: runs });
187
+ }
188
+ return result;
189
+ }
@@ -0,0 +1,2 @@
1
+ import { ApiResponse } from './constants.js';
2
+ export declare function callApi(questionText: string, api: any, sessionId: string, action: string): Promise<ApiResponse>;
@@ -0,0 +1,107 @@
1
+ /*
2
+ * 版权所有 (c) 华为技术有限公司 2026-2026
3
+ */
4
+ import https from 'https';
5
+ import { URL } from 'url';
6
+ import { getConfig } from './config.js';
7
+ import { DEFAULT_HTTP_PORT, HTTP_STATUS_BAD_REQUEST, API_URL_SUFFIX } from './constants.js';
8
+ function buildHeadersForCelia(config, sessionId) {
9
+ if (!config.uid || !config.apiKey || !config.skillId || !config.requestFrom) {
10
+ throw new Error('[SENTINEL HOOK] Missing required configuration: uid, apiKey, skillId, or requestFrom is not defined');
11
+ }
12
+ return {
13
+ 'x-hag-trace-id': sessionId,
14
+ 'x-uid': config.uid,
15
+ 'x-api-key': config.apiKey,
16
+ 'x-request-from': config.requestFrom,
17
+ 'x-skill-id': config.skillId,
18
+ 'content-type': 'application/json'
19
+ };
20
+ }
21
+ function buildRequestOptions(url, headers, timeout) {
22
+ const urlObj = new URL(url);
23
+ return {
24
+ hostname: urlObj.hostname,
25
+ port: urlObj.port || DEFAULT_HTTP_PORT,
26
+ path: urlObj.pathname,
27
+ method: "POST",
28
+ headers: headers,
29
+ timeout: timeout
30
+ };
31
+ }
32
+ function checkHttpStatus(res) {
33
+ if (res.statusCode && res.statusCode >= HTTP_STATUS_BAD_REQUEST) {
34
+ throw new Error(`HTTP error! status: ${res.statusCode}`);
35
+ }
36
+ }
37
+ function parseResponseData(data) {
38
+ try {
39
+ if (data === undefined || data === null || data.trim() === '') {
40
+ throw new Error('API response data is empty or invalid');
41
+ }
42
+ const jsonData = JSON.parse(data);
43
+ if (jsonData.retCode && jsonData.retCode !== "0") {
44
+ const errorMsg = jsonData.retMsg || 'Unknown API error';
45
+ throw new Error(`API error: ${errorMsg}`);
46
+ }
47
+ if (!jsonData.retCode && jsonData.code) {
48
+ const errorMsg = jsonData.desc || 'Unknown backend error';
49
+ throw new Error(`Backend error: ${errorMsg}`);
50
+ }
51
+ return jsonData;
52
+ }
53
+ catch (e) {
54
+ if (e instanceof Error) {
55
+ throw new Error(`[SENTINEL HOOK] Failed to parse response:${e.message}`);
56
+ }
57
+ return data;
58
+ }
59
+ }
60
+ function handleResponse(res, resolve, reject) {
61
+ let data = '';
62
+ try {
63
+ checkHttpStatus(res);
64
+ }
65
+ catch (e) {
66
+ reject(e);
67
+ }
68
+ res.on('data', (chunk) => {
69
+ data += chunk;
70
+ });
71
+ res.on('end', () => {
72
+ try {
73
+ const result = parseResponseData(data);
74
+ resolve(result);
75
+ }
76
+ catch (e) {
77
+ reject(e);
78
+ }
79
+ });
80
+ }
81
+ export async function callApi(questionText, api, sessionId, action) {
82
+ const config = getConfig(api);
83
+ const headersForCelia = buildHeadersForCelia(config, sessionId);
84
+ const payload = {
85
+ questionText: questionText,
86
+ textSource: config.textSource,
87
+ action: action,
88
+ extra: `${JSON.stringify({ userId: config.uid })}`
89
+ };
90
+ const httpBody = JSON.stringify(payload);
91
+ const apiUrl = `${config.api.url}${API_URL_SUFFIX}`;
92
+ return new Promise((resolve, reject) => {
93
+ const options = buildRequestOptions(apiUrl, headersForCelia, config.api.timeout);
94
+ const req = https.request(options, (res) => {
95
+ handleResponse(res, resolve, reject);
96
+ });
97
+ req.on('error', (error) => {
98
+ reject(error);
99
+ });
100
+ req.on('timeout', () => {
101
+ req.destroy();
102
+ reject(new Error('[SENTINEL HOOK] Request timeout'));
103
+ });
104
+ req.write(httpBody);
105
+ req.end();
106
+ });
107
+ }
@@ -1,11 +1,11 @@
1
- import type { ClawdbotConfig } from "openclaw/plugin-sdk";
2
- import type { XYChannelConfig } from "../types.js";
1
+ import { HttpHeaders } from './constants.js';
3
2
  export interface ApiConfig {
4
3
  url: string;
5
4
  timeout: number;
6
5
  }
7
- export interface CsplConfig {
6
+ export interface Config {
8
7
  api: ApiConfig;
8
+ headers?: HttpHeaders;
9
9
  uid: string;
10
10
  apiKey: string;
11
11
  skillId: string;
@@ -13,17 +13,4 @@ export interface CsplConfig {
13
13
  textSource: string;
14
14
  action: string;
15
15
  }
16
- /**
17
- * 构建 CSPL 配置。uid 和 apiKey 复用 XYChannelConfig,避免重复配置。
18
- * serviceUrl 从 .xiaoyienv 文件读取,skillId 写死在常量中。
19
- *
20
- * Accepts either ClawdbotConfig (legacy after_tool_call path) or
21
- * XYChannelConfig (AgentToolResultMiddleware path). Config is cached
22
- * after the first successful call so subsequent calls can omit the arg.
23
- */
24
- export declare function getCsplConfig(cfg?: ClawdbotConfig): CsplConfig;
25
- /**
26
- * Initialize CSPL config from an already-resolved XYChannelConfig.
27
- * Used by AgentToolResultMiddleware which has session context but not ClawdbotConfig.
28
- */
29
- export declare function initCsplConfigFromXYConfig(xyConfig: XYChannelConfig): CsplConfig;
16
+ export declare function getConfig(api: any): Config;