@ynhcj/xiaoyi-channel 0.0.210-beta → 0.0.211-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/channel.js +2 -0
- package/dist/src/tools/hmos-cli.js +3 -2
- package/dist/src/tools/invoke.d.ts +49 -9
- package/dist/src/tools/invoke.js +120 -120
- package/package.json +1 -1
package/dist/src/channel.js
CHANGED
|
@@ -27,6 +27,7 @@ import { discoverCrossDevicesTool } from "./tools/discover-cross-devices-tool.js
|
|
|
27
27
|
import { sendCrossDeviceTaskTool } from "./tools/send-cross-device-task-tool.js";
|
|
28
28
|
import { displayA2UICardByPathTool } from "./tools/display-a2ui-card-bypath-tool.js";
|
|
29
29
|
import { checkPluginPrivilegeTool } from "./tools/check-plugin-privilege-tool.js";
|
|
30
|
+
import { invokeTool } from "./tools/invoke.js";
|
|
30
31
|
const ALL_TOOLS = [
|
|
31
32
|
locationTool,
|
|
32
33
|
discoverCrossDevicesTool,
|
|
@@ -50,6 +51,7 @@ const ALL_TOOLS = [
|
|
|
50
51
|
loginTokenTool,
|
|
51
52
|
agentAsSkillTool,
|
|
52
53
|
checkPluginPrivilegeTool,
|
|
54
|
+
invokeTool,
|
|
53
55
|
];
|
|
54
56
|
/**
|
|
55
57
|
* Xiaoyi Channel Plugin for OpenClaw.
|
|
@@ -15,6 +15,7 @@ import path from "path";
|
|
|
15
15
|
import os from "os";
|
|
16
16
|
import { logger } from "../utils/logger.js";
|
|
17
17
|
import { InvokeError, invokeErrorToResult } from "./invoke.js";
|
|
18
|
+
import { getCurrentSessionContext } from "./session-manager.js";
|
|
18
19
|
import { getXYWebSocketManager } from "../client.js";
|
|
19
20
|
import { sendCommand } from "../formatter.js";
|
|
20
21
|
import { getCurrentTaskId } from "../task-manager.js";
|
|
@@ -534,8 +535,8 @@ export async function cliBeforeToolCallHandler(event, ctx) {
|
|
|
534
535
|
},
|
|
535
536
|
};
|
|
536
537
|
}
|
|
537
|
-
// Get session context
|
|
538
|
-
const sessionCtx =
|
|
538
|
+
// Get session context via ALS (propagated through the async call chain)
|
|
539
|
+
const sessionCtx = getCurrentSessionContext();
|
|
539
540
|
if (!sessionCtx) {
|
|
540
541
|
logger.warn(`[CLI-HOOK] No session context, blocking (${(performance.now() - t0).toFixed(1)}ms)`);
|
|
541
542
|
return { block: true, blockReason: JSON.stringify({ code: "CONFIG_MISSING", message: "No active session context for CLI execution", retryable: false }) };
|
|
@@ -18,8 +18,6 @@
|
|
|
18
18
|
* 7. Device executor
|
|
19
19
|
* 8. Invoke tool factory (public API)
|
|
20
20
|
*/
|
|
21
|
-
import type { ChannelAgentTool } from "openclaw/plugin-sdk";
|
|
22
|
-
import type { SessionContext } from "./session-manager.js";
|
|
23
21
|
export type InvokeErrorCode = "INVALID_PARAM" | "INVALID_TOOL_DEFINITION" | "CONFIG_MISSING" | "AUTH_FAIL" | "PERMISSION_DENIED" | "CLI_COMMAND_BLOCKED" | "RATE_LIMIT" | "TIMEOUT" | "TOOL_NOT_FOUND" | "TOOL_CONFLICT" | "UNSUPPORTED_PLUGIN_TYPE" | "UNSUPPORTED_PROTOCOL" | "DEVICE_TOOL_BLOCKED" | "UPSTREAM_ERROR" | "NETWORK_ERROR" | "UNKNOWN";
|
|
24
22
|
interface ExecuteResult {
|
|
25
23
|
content: Array<{
|
|
@@ -38,11 +36,53 @@ export declare class InvokeError extends Error {
|
|
|
38
36
|
get status(): number;
|
|
39
37
|
}
|
|
40
38
|
export declare function invokeErrorToResult(error: InvokeError): ExecuteResult;
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
39
|
+
export declare const invokeTool: {
|
|
40
|
+
name: string;
|
|
41
|
+
label: string;
|
|
42
|
+
description: string;
|
|
43
|
+
parameters: {
|
|
44
|
+
type: string;
|
|
45
|
+
properties: {
|
|
46
|
+
functionName: {
|
|
47
|
+
type: string;
|
|
48
|
+
minLength: number;
|
|
49
|
+
description: string;
|
|
50
|
+
};
|
|
51
|
+
funcName: {
|
|
52
|
+
type: string;
|
|
53
|
+
minLength: number;
|
|
54
|
+
description: string;
|
|
55
|
+
};
|
|
56
|
+
arguments: {
|
|
57
|
+
type: string;
|
|
58
|
+
description: string;
|
|
59
|
+
properties: {
|
|
60
|
+
bundleName: {
|
|
61
|
+
type: string;
|
|
62
|
+
minLength: number;
|
|
63
|
+
description: string;
|
|
64
|
+
};
|
|
65
|
+
};
|
|
66
|
+
required: string[];
|
|
67
|
+
additionalProperties: boolean;
|
|
68
|
+
};
|
|
69
|
+
params: {
|
|
70
|
+
type: string;
|
|
71
|
+
description: string;
|
|
72
|
+
properties: {
|
|
73
|
+
bundleName: {
|
|
74
|
+
type: string;
|
|
75
|
+
minLength: number;
|
|
76
|
+
description: string;
|
|
77
|
+
};
|
|
78
|
+
};
|
|
79
|
+
required: string[];
|
|
80
|
+
additionalProperties: boolean;
|
|
81
|
+
};
|
|
82
|
+
};
|
|
83
|
+
required: any[];
|
|
84
|
+
additionalProperties: boolean;
|
|
85
|
+
};
|
|
86
|
+
execute(toolCallId: string, rawParams: unknown, _signal?: AbortSignal, _onUpdate?: (partialResult: any) => void): Promise<ExecuteResult>;
|
|
87
|
+
};
|
|
48
88
|
export {};
|
package/dist/src/tools/invoke.js
CHANGED
|
@@ -27,6 +27,7 @@ import { logger } from "../utils/logger.js";
|
|
|
27
27
|
import { getXYWebSocketManager } from "../client.js";
|
|
28
28
|
import { sendCommand } from "../formatter.js";
|
|
29
29
|
import { getCurrentTaskId } from "../task-manager.js";
|
|
30
|
+
import { getCurrentSessionContext } from "./session-manager.js";
|
|
30
31
|
const RETRYABLE_CODES = new Set([
|
|
31
32
|
"RATE_LIMIT",
|
|
32
33
|
"TIMEOUT",
|
|
@@ -1066,136 +1067,135 @@ function validateBusinessParams(businessParams, definition) {
|
|
|
1066
1067
|
}
|
|
1067
1068
|
return null;
|
|
1068
1069
|
}
|
|
1069
|
-
|
|
1070
|
-
|
|
1071
|
-
|
|
1072
|
-
|
|
1073
|
-
|
|
1074
|
-
|
|
1075
|
-
|
|
1076
|
-
|
|
1077
|
-
|
|
1078
|
-
|
|
1079
|
-
|
|
1080
|
-
|
|
1081
|
-
|
|
1082
|
-
|
|
1083
|
-
|
|
1084
|
-
|
|
1085
|
-
|
|
1086
|
-
|
|
1087
|
-
|
|
1088
|
-
|
|
1089
|
-
|
|
1090
|
-
|
|
1091
|
-
|
|
1092
|
-
|
|
1093
|
-
|
|
1094
|
-
|
|
1095
|
-
|
|
1096
|
-
|
|
1097
|
-
bundleName: {
|
|
1098
|
-
type: "string",
|
|
1099
|
-
minLength: 1,
|
|
1100
|
-
description: "HarmonyOS 应用唯一标识,如 com.example.weather。",
|
|
1101
|
-
},
|
|
1070
|
+
// ═══════════════════════════════════════════════════════════════════════════
|
|
1071
|
+
// 8. Invoke tool (static — matches ALL_TOOLS pattern)
|
|
1072
|
+
// ═══════════════════════════════════════════════════════════════════════════
|
|
1073
|
+
export const invokeTool = {
|
|
1074
|
+
name: "invoke",
|
|
1075
|
+
label: "Invoke",
|
|
1076
|
+
description: "调用已安装 skill 中声明的工具。必须传 functionName(或funcName) 与 arguments(或params);functionName 的值等于工具定义中的 toolName;arguments 是包含 bundleName 和业务参数字段的对象;完整业务参数定义见 references/tools/<bundleName>__<toolName>.json。",
|
|
1077
|
+
parameters: {
|
|
1078
|
+
type: "object",
|
|
1079
|
+
properties: {
|
|
1080
|
+
functionName: {
|
|
1081
|
+
type: "string",
|
|
1082
|
+
minLength: 1,
|
|
1083
|
+
description: "工具名称,值等于对应 references/tools JSON 中的 toolName,如 weather_query(优先使用;funcName 已废弃但仍兼容)。",
|
|
1084
|
+
},
|
|
1085
|
+
funcName: {
|
|
1086
|
+
type: "string",
|
|
1087
|
+
minLength: 1,
|
|
1088
|
+
description: "[废弃] 请使用 functionName 替代。工具名称,值等于对应 references/tools JSON 中的 toolName。",
|
|
1089
|
+
},
|
|
1090
|
+
arguments: {
|
|
1091
|
+
type: "object",
|
|
1092
|
+
description: "包含定位字段 bundleName 与业务参数字段;除 bundleName 外的字段遵循对应 references/tools JSON 中的 arguments schema(优先使用;params 已废弃但仍兼容)。",
|
|
1093
|
+
properties: {
|
|
1094
|
+
bundleName: {
|
|
1095
|
+
type: "string",
|
|
1096
|
+
minLength: 1,
|
|
1097
|
+
description: "HarmonyOS 应用唯一标识,如 com.example.weather。",
|
|
1102
1098
|
},
|
|
1103
|
-
required: ["bundleName"],
|
|
1104
|
-
additionalProperties: true,
|
|
1105
1099
|
},
|
|
1106
|
-
|
|
1107
|
-
|
|
1108
|
-
|
|
1109
|
-
|
|
1110
|
-
|
|
1111
|
-
|
|
1112
|
-
|
|
1113
|
-
|
|
1114
|
-
|
|
1100
|
+
required: ["bundleName"],
|
|
1101
|
+
additionalProperties: true,
|
|
1102
|
+
},
|
|
1103
|
+
params: {
|
|
1104
|
+
type: "object",
|
|
1105
|
+
description: "[废弃] 请使用 arguments 替代。包含定位字段 bundleName 与业务参数字段。",
|
|
1106
|
+
properties: {
|
|
1107
|
+
bundleName: {
|
|
1108
|
+
type: "string",
|
|
1109
|
+
minLength: 1,
|
|
1110
|
+
description: "HarmonyOS 应用唯一标识,如 com.example.weather。",
|
|
1115
1111
|
},
|
|
1116
|
-
required: ["bundleName"],
|
|
1117
|
-
additionalProperties: true,
|
|
1118
1112
|
},
|
|
1113
|
+
required: ["bundleName"],
|
|
1114
|
+
additionalProperties: true,
|
|
1119
1115
|
},
|
|
1120
|
-
required: [],
|
|
1121
|
-
additionalProperties: false,
|
|
1122
1116
|
},
|
|
1123
|
-
|
|
1124
|
-
|
|
1125
|
-
|
|
1126
|
-
|
|
1127
|
-
|
|
1128
|
-
|
|
1129
|
-
|
|
1130
|
-
|
|
1131
|
-
|
|
1132
|
-
|
|
1133
|
-
|
|
1134
|
-
|
|
1135
|
-
|
|
1136
|
-
|
|
1137
|
-
|
|
1138
|
-
|
|
1139
|
-
|
|
1140
|
-
|
|
1141
|
-
|
|
1142
|
-
|
|
1143
|
-
|
|
1144
|
-
|
|
1145
|
-
|
|
1146
|
-
|
|
1147
|
-
|
|
1148
|
-
|
|
1117
|
+
required: [],
|
|
1118
|
+
additionalProperties: false,
|
|
1119
|
+
},
|
|
1120
|
+
async execute(toolCallId, rawParams, _signal, _onUpdate) {
|
|
1121
|
+
const ctx = getCurrentSessionContext();
|
|
1122
|
+
// Layer 1: input validation
|
|
1123
|
+
const validated = validateAndExtract(rawParams);
|
|
1124
|
+
if ("content" in validated) {
|
|
1125
|
+
logger.warn("[INVOKE] input validation failed", { toolCallId });
|
|
1126
|
+
return validated;
|
|
1127
|
+
}
|
|
1128
|
+
const { toolName, bundleName, businessParams } = validated;
|
|
1129
|
+
const businessKeys = Object.keys(businessParams);
|
|
1130
|
+
logger.log("[INVOKE] tool called", {
|
|
1131
|
+
toolCallId, toolName, bundleName,
|
|
1132
|
+
businessKeys: businessKeys.join(","),
|
|
1133
|
+
businessKeysCount: businessKeys.length,
|
|
1134
|
+
});
|
|
1135
|
+
// Layer 2: cache lookup
|
|
1136
|
+
const cache = getToolCache();
|
|
1137
|
+
const entry = cache.get(bundleName, toolName);
|
|
1138
|
+
if (!entry) {
|
|
1139
|
+
const conflict = cache.getConflict(bundleName, toolName);
|
|
1140
|
+
if (conflict) {
|
|
1141
|
+
const files = conflict.entries.map((e) => e.filePath).join(", ");
|
|
1142
|
+
logger.warn("[INVOKE] tool conflict", { toolCallId, toolName, bundleName, conflictCount: conflict.entries.length });
|
|
1143
|
+
return invokeErrorToResult(new InvokeError("TOOL_CONFLICT", `Multiple conflicting definitions for '${toolName}' in bundle '${bundleName}': ${files}`));
|
|
1149
1144
|
}
|
|
1150
|
-
|
|
1151
|
-
|
|
1152
|
-
|
|
1153
|
-
|
|
1154
|
-
|
|
1155
|
-
|
|
1156
|
-
|
|
1157
|
-
|
|
1158
|
-
|
|
1159
|
-
|
|
1160
|
-
|
|
1145
|
+
logger.warn("[INVOKE] tool not found", { toolCallId, toolName, bundleName });
|
|
1146
|
+
return invokeErrorToResult(new InvokeError("TOOL_NOT_FOUND", `Tool '${toolName}' not found in bundle '${bundleName}'.`));
|
|
1147
|
+
}
|
|
1148
|
+
const { definition, skillName } = entry;
|
|
1149
|
+
logger.log("[INVOKE] cache hit", {
|
|
1150
|
+
toolCallId, toolName, bundleName, skillName,
|
|
1151
|
+
pluginType: definition.pluginType,
|
|
1152
|
+
protocol: definition.protocol ?? "N/A",
|
|
1153
|
+
});
|
|
1154
|
+
// Layer 2b: business param validation
|
|
1155
|
+
const paramError = validateBusinessParams(businessParams, definition);
|
|
1156
|
+
if (paramError) {
|
|
1157
|
+
logger.warn("[INVOKE] business param validation failed", { toolCallId, toolName, bundleName });
|
|
1158
|
+
return paramError;
|
|
1159
|
+
}
|
|
1160
|
+
// Layer 3: execute
|
|
1161
|
+
const pluginType = definition.pluginType;
|
|
1162
|
+
logger.log(`[INVOKE] dispatching to ${pluginType} executor`, { toolCallId, toolName, bundleName, pluginType });
|
|
1163
|
+
try {
|
|
1164
|
+
if (pluginType === "Cloud" || pluginType === "MCP") {
|
|
1165
|
+
const result = await executeCloudTool({ definition, businessParams, skillName, sessionId: ctx?.sessionId ?? "", agentId: ctx?.agentId ?? "" });
|
|
1166
|
+
logger.log("[INVOKE] cloud execution succeeded", {
|
|
1167
|
+
toolCallId, toolName, bundleName, pluginType,
|
|
1168
|
+
resultLength: result.content[0]?.text?.length ?? 0,
|
|
1169
|
+
});
|
|
1170
|
+
return result;
|
|
1161
1171
|
}
|
|
1162
|
-
|
|
1163
|
-
|
|
1164
|
-
|
|
1165
|
-
try {
|
|
1166
|
-
if (pluginType === "Cloud" || pluginType === "MCP") {
|
|
1167
|
-
const result = await executeCloudTool({ definition, businessParams, skillName, sessionId: ctx.sessionId, agentId: ctx.agentId });
|
|
1168
|
-
logger.log("[INVOKE] cloud execution succeeded", {
|
|
1169
|
-
toolCallId, toolName, bundleName, pluginType,
|
|
1170
|
-
resultLength: result.content[0]?.text?.length ?? 0,
|
|
1171
|
-
});
|
|
1172
|
-
return result;
|
|
1173
|
-
}
|
|
1174
|
-
if (pluginType === "Device") {
|
|
1175
|
-
const result = await executeDeviceTool({ definition, businessParams, toolCallId }, ctx);
|
|
1176
|
-
logger.log("[INVOKE] device execution succeeded", {
|
|
1177
|
-
toolCallId, toolName, bundleName,
|
|
1178
|
-
resultLength: result.content[0]?.text?.length ?? 0,
|
|
1179
|
-
});
|
|
1180
|
-
return result;
|
|
1172
|
+
if (pluginType === "Device") {
|
|
1173
|
+
if (!ctx) {
|
|
1174
|
+
return invokeErrorToResult(new InvokeError("DEVICE_TOOL_BLOCKED", "Device tools require an active session context."));
|
|
1181
1175
|
}
|
|
1182
|
-
|
|
1183
|
-
|
|
1176
|
+
const result = await executeDeviceTool({ definition, businessParams, toolCallId }, ctx);
|
|
1177
|
+
logger.log("[INVOKE] device execution succeeded", {
|
|
1178
|
+
toolCallId, toolName, bundleName,
|
|
1179
|
+
resultLength: result.content[0]?.text?.length ?? 0,
|
|
1180
|
+
});
|
|
1181
|
+
return result;
|
|
1184
1182
|
}
|
|
1185
|
-
|
|
1186
|
-
|
|
1187
|
-
|
|
1188
|
-
|
|
1189
|
-
|
|
1190
|
-
|
|
1191
|
-
return invokeErrorToResult(err);
|
|
1192
|
-
}
|
|
1193
|
-
logger.error("[INVOKE] unexpected execution error", {
|
|
1183
|
+
logger.warn("[INVOKE] unsupported pluginType", { toolCallId, toolName, bundleName, pluginType });
|
|
1184
|
+
return invokeErrorToResult(new InvokeError("UNSUPPORTED_PLUGIN_TYPE", `Unsupported pluginType '${pluginType}'. Must be Cloud, Device, or MCP.`));
|
|
1185
|
+
}
|
|
1186
|
+
catch (err) {
|
|
1187
|
+
if (err instanceof InvokeError) {
|
|
1188
|
+
logger.warn("[INVOKE] invocation error", {
|
|
1194
1189
|
toolCallId, toolName, bundleName, pluginType,
|
|
1195
|
-
|
|
1190
|
+
errorCode: err.code, errorMessage: err.message, retryable: err.retryable,
|
|
1196
1191
|
});
|
|
1197
|
-
return
|
|
1192
|
+
return invokeErrorToResult(err);
|
|
1198
1193
|
}
|
|
1199
|
-
|
|
1200
|
-
|
|
1201
|
-
}
|
|
1194
|
+
logger.error("[INVOKE] unexpected execution error", {
|
|
1195
|
+
toolCallId, toolName, bundleName, pluginType,
|
|
1196
|
+
error: err instanceof Error ? `${err.name}: ${err.message}` : String(err),
|
|
1197
|
+
});
|
|
1198
|
+
return unknownErrorToResult(err);
|
|
1199
|
+
}
|
|
1200
|
+
},
|
|
1201
|
+
};
|