@ynhcj/xiaoyi-channel 0.0.78-beta → 0.0.79-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/index.js +5 -4
- package/dist/src/cspl/call-api.js +14 -11
- package/dist/src/cspl/config.js +3 -3
- package/dist/src/cspl/constants.d.ts +1 -0
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -20,13 +20,13 @@ const plugin = {
|
|
|
20
20
|
setXYRuntime(api.runtime);
|
|
21
21
|
api.registerChannel({ plugin: xyPlugin });
|
|
22
22
|
api.registerProvider(xiaoyiProvider);
|
|
23
|
-
//
|
|
23
|
+
// SENTINEL HOOK after_tool_call hook: 监听工具结果,发送至安全检测 API 进行安全检测
|
|
24
24
|
// 如果响应为 REJECT,注入 steer 消息中止当前对话
|
|
25
25
|
api.on("after_tool_call", async (event, ctx) => {
|
|
26
26
|
if (!ALLOWED_TOOLS.includes(event.toolName)) {
|
|
27
27
|
return;
|
|
28
28
|
}
|
|
29
|
-
console.log(`[
|
|
29
|
+
console.log(`[SENTINEL HOOK] after_tool_call triggered: toolName=${event.toolName}, sessionKey=${ctx.sessionKey ?? "none"}`);
|
|
30
30
|
try {
|
|
31
31
|
const resultText = extractResultText(event, event.toolName);
|
|
32
32
|
const resultLength = resultText.length;
|
|
@@ -35,6 +35,7 @@ const plugin = {
|
|
|
35
35
|
}
|
|
36
36
|
// 构造 sentinel_hook 格式的 payload: { tool, output: [{ content }] }
|
|
37
37
|
const questionText = {
|
|
38
|
+
subSceneID: 'TOOL_OUTPUT',
|
|
38
39
|
tool: event.toolName,
|
|
39
40
|
output: [{ content: "" }],
|
|
40
41
|
};
|
|
@@ -49,13 +50,13 @@ const plugin = {
|
|
|
49
50
|
}
|
|
50
51
|
const response = await callCsplApi(finalJson, api.config);
|
|
51
52
|
const result = parseSecurityResult(response);
|
|
52
|
-
console.log(`[
|
|
53
|
+
console.log(`[SENTINEL HOOK] Security result: status=${result.status}`);
|
|
53
54
|
if (result.status === "REJECT") {
|
|
54
55
|
await tryInjectSteer(ctx.sessionKey, STEER_ABORT_MESSAGE);
|
|
55
56
|
}
|
|
56
57
|
}
|
|
57
58
|
catch (err) {
|
|
58
|
-
api.logger.error(`[
|
|
59
|
+
api.logger.error(`[SENTINEL HOOK] after_tool_call error: ${err}`);
|
|
59
60
|
}
|
|
60
61
|
});
|
|
61
62
|
},
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
//
|
|
1
|
+
// SENTINEL HOOK API 请求模块
|
|
2
2
|
import https from "node:https";
|
|
3
3
|
import { URL } from "node:url";
|
|
4
4
|
import { randomBytes } from "node:crypto";
|
|
@@ -8,8 +8,10 @@ function generateTraceId() {
|
|
|
8
8
|
return randomBytes(16).toString("hex");
|
|
9
9
|
}
|
|
10
10
|
function buildHeaders(config) {
|
|
11
|
+
const traceId = generateTraceId();
|
|
12
|
+
console.log(`[SENTINEL HOOK] trace-id: ${traceId}`);
|
|
11
13
|
return {
|
|
12
|
-
"x-hag-trace-id":
|
|
14
|
+
"x-hag-trace-id": traceId,
|
|
13
15
|
"x-uid": config.uid,
|
|
14
16
|
"x-api-key": config.apiKey,
|
|
15
17
|
"x-request-from": config.requestFrom,
|
|
@@ -30,13 +32,13 @@ function buildRequestOptions(url, headers, timeout) {
|
|
|
30
32
|
}
|
|
31
33
|
function parseResponse(data) {
|
|
32
34
|
if (!data?.trim())
|
|
33
|
-
throw new Error("[
|
|
35
|
+
throw new Error("[SENTINEL HOOK] API response is empty");
|
|
34
36
|
const json = JSON.parse(data);
|
|
35
37
|
if (json.retCode && json.retCode !== "0") {
|
|
36
|
-
throw new Error(`[
|
|
38
|
+
throw new Error(`[SENTINEL HOOK] API error: ${json.retMsg || "unknown"}`);
|
|
37
39
|
}
|
|
38
40
|
if (!json.retCode && json.code) {
|
|
39
|
-
throw new Error(`[
|
|
41
|
+
throw new Error(`[SENTINEL HOOK] Backend error: ${json.desc || "unknown"}`);
|
|
40
42
|
}
|
|
41
43
|
return json;
|
|
42
44
|
}
|
|
@@ -47,12 +49,13 @@ export async function callCsplApi(questionText, cfg) {
|
|
|
47
49
|
questionText,
|
|
48
50
|
textSource: config.textSource,
|
|
49
51
|
action: config.action,
|
|
52
|
+
extra: JSON.stringify({ userId: config.uid }),
|
|
50
53
|
};
|
|
51
54
|
return new Promise((resolve, reject) => {
|
|
52
55
|
const options = buildRequestOptions(config.api.url, headers, config.api.timeout);
|
|
53
56
|
const req = https.request(options, (res) => {
|
|
54
57
|
if (res.statusCode && res.statusCode >= HTTP_STATUS_BAD_REQUEST) {
|
|
55
|
-
reject(new Error(`[
|
|
58
|
+
reject(new Error(`[SENTINEL HOOK] HTTP error: ${res.statusCode}`));
|
|
56
59
|
return;
|
|
57
60
|
}
|
|
58
61
|
let data = "";
|
|
@@ -62,23 +65,23 @@ export async function callCsplApi(questionText, cfg) {
|
|
|
62
65
|
res.on("end", () => {
|
|
63
66
|
try {
|
|
64
67
|
const result = parseResponse(data);
|
|
65
|
-
console.log(`[
|
|
68
|
+
console.log(`[SENTINEL HOOK] ✅ 请求成功`);
|
|
66
69
|
resolve(result);
|
|
67
70
|
}
|
|
68
71
|
catch (e) {
|
|
69
|
-
console.error(`[
|
|
72
|
+
console.error(`[SENTINEL HOOK] ❌ 请求失败: ${e instanceof Error ? e.message : String(e)}`);
|
|
70
73
|
reject(e);
|
|
71
74
|
}
|
|
72
75
|
});
|
|
73
76
|
});
|
|
74
77
|
req.on("error", (error) => {
|
|
75
|
-
console.error(`[
|
|
78
|
+
console.error(`[SENTINEL HOOK] ❌ 请求错误: ${error instanceof Error ? error.message : String(error)}`);
|
|
76
79
|
reject(error);
|
|
77
80
|
});
|
|
78
81
|
req.on("timeout", () => {
|
|
79
|
-
console.error(`[
|
|
82
|
+
console.error(`[SENTINEL HOOK] ⏰ 请求超时 (${config.api.timeout}ms)`);
|
|
80
83
|
req.destroy();
|
|
81
|
-
reject(new Error("[
|
|
84
|
+
reject(new Error("[SENTINEL HOOK] Request timeout"));
|
|
82
85
|
});
|
|
83
86
|
req.write(JSON.stringify(payload));
|
|
84
87
|
req.end();
|
package/dist/src/cspl/config.js
CHANGED
|
@@ -7,7 +7,7 @@ import { logger } from "../utils/logger.js";
|
|
|
7
7
|
let cachedConfig = null;
|
|
8
8
|
function readServiceUrl() {
|
|
9
9
|
if (!fs.existsSync(ENV_FILE_PATH)) {
|
|
10
|
-
throw new Error(`[
|
|
10
|
+
throw new Error(`[SENTINEL HOOK] Environment file not found: ${ENV_FILE_PATH}`);
|
|
11
11
|
}
|
|
12
12
|
const envData = fs.readFileSync(ENV_FILE_PATH, "utf-8");
|
|
13
13
|
for (const line of envData.split("\n")) {
|
|
@@ -22,7 +22,7 @@ function readServiceUrl() {
|
|
|
22
22
|
if (key === "SERVICE_URL" && value)
|
|
23
23
|
return value;
|
|
24
24
|
}
|
|
25
|
-
throw new Error("[
|
|
25
|
+
throw new Error("[SENTINEL HOOK] Missing SERVICE_URL in env file");
|
|
26
26
|
}
|
|
27
27
|
/**
|
|
28
28
|
* 构建 CSPL 配置。uid 和 apiKey 复用 XYChannelConfig,避免重复配置。
|
|
@@ -45,6 +45,6 @@ export function getCsplConfig(cfg) {
|
|
|
45
45
|
textSource: CSPL_STATIC_CONFIG.textSource,
|
|
46
46
|
action: CSPL_STATIC_CONFIG.action,
|
|
47
47
|
};
|
|
48
|
-
logger.log("[
|
|
48
|
+
logger.log("[SENTINEL HOOK] Config loaded (uid/apiKey from XYChannelConfig)");
|
|
49
49
|
return cachedConfig;
|
|
50
50
|
}
|