tmp
This commit is contained in:
@@ -2,4 +2,3 @@
|
|||||||
/logs/
|
/logs/
|
||||||
/data/
|
/data/
|
||||||
.env
|
.env
|
||||||
conf.json
|
|
||||||
|
|||||||
+47
-29
@@ -1,8 +1,10 @@
|
|||||||
import 'dotenv/config';
|
import 'dotenv/config';
|
||||||
import { createDeepAgent, FilesystemBackend } from "deepagents";
|
import { createDeepAgent, FilesystemBackend } from "deepagents";
|
||||||
import { AIMessageChunk, ToolMessage } from "langchain";
|
import { AIMessageChunk, ToolMessage } from "langchain";
|
||||||
import { SystemMessage, HumanMessage, AIMessage } from "@langchain/core/messages";
|
import { HumanMessage } from "@langchain/core/messages";
|
||||||
import { ChatDeepSeek } from "@langchain/deepseek";
|
import { ChatDeepSeek } from "@langchain/deepseek";
|
||||||
|
import config from '../../conf.json' with { type: 'json' };
|
||||||
|
import logger from '../../utils/logger.js';
|
||||||
import EscortAdminPrompts from "./prompts.js";
|
import EscortAdminPrompts from "./prompts.js";
|
||||||
import {
|
import {
|
||||||
getEnvTool, webFetchTool, webSearchTool, getCalendarInfoTool,
|
getEnvTool, webFetchTool, webSearchTool, getCalendarInfoTool,
|
||||||
@@ -12,10 +14,14 @@ import {
|
|||||||
|
|
||||||
export default class EscortAdminAgent {
|
export default class EscortAdminAgent {
|
||||||
constructor() {
|
constructor() {
|
||||||
|
// 按用户隔离会话:userId -> { agent, messages }
|
||||||
|
this.sessions = new Map();
|
||||||
|
this.maxSessions = 100;
|
||||||
}
|
}
|
||||||
|
|
||||||
clearMessages() {
|
clearMessages(userInfo) {
|
||||||
this.messages = [];
|
const userId = userInfo?._id ?? 'default-session';
|
||||||
|
this.sessions.delete(userId);
|
||||||
}
|
}
|
||||||
|
|
||||||
// msg: { agent: 'escort-admin', type: 'chat', ts: "2023-08-01 10:00:00", content: "你好" }
|
// msg: { agent: 'escort-admin', type: 'chat', ts: "2023-08-01 10:00:00", content: "你好" }
|
||||||
@@ -24,27 +30,47 @@ export default class EscortAdminAgent {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const agent = this._genAgent(userInfo);
|
const userId = userInfo?._id ?? 'default-session';
|
||||||
|
|
||||||
|
// LRU:按用户取会话,并更新使用顺序
|
||||||
|
let session = this.sessions.get(userId);
|
||||||
|
if (session) {
|
||||||
|
this.sessions.delete(userId);
|
||||||
|
} else {
|
||||||
|
session = { agent: null, messages: [] };
|
||||||
|
}
|
||||||
|
this.sessions.set(userId, session);
|
||||||
|
|
||||||
|
// 超出上限时淘汰最久未使用的会话,避免内存泄漏
|
||||||
|
if (this.sessions.size > this.maxSessions) {
|
||||||
|
const oldest = this.sessions.keys().next().value;
|
||||||
|
this.sessions.delete(oldest);
|
||||||
|
}
|
||||||
|
|
||||||
msgs.forEach(msg => {
|
msgs.forEach(msg => {
|
||||||
if (msg.type === "clear") {
|
if (msg.type === "clear") {
|
||||||
this.messages = [];
|
session.messages = [];
|
||||||
|
session.agent = null;
|
||||||
} else {
|
} else {
|
||||||
this.messages.push(new HumanMessage(`${msg.ts} - ${msg.content}`));
|
session.messages.push(new HumanMessage(`${msg.ts} - ${msg.content}`));
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
if (this.messages.length === 0) {
|
if (session.messages.length === 0) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const agent = session.agent ?? this._genAgent(userInfo);
|
||||||
|
session.agent = agent;
|
||||||
|
|
||||||
const INTERESTING_NODES = new Set(["model_request", "tools"]);
|
const INTERESTING_NODES = new Set(["model_request", "tools"]);
|
||||||
for await (const [namespace, mode, data] of await agent.stream(
|
for await (const [namespace, mode, data] of await agent.stream(
|
||||||
{ messages: this.messages },
|
{ messages: session.messages },
|
||||||
{
|
{
|
||||||
recursion_limit: 50,
|
recursion_limit: 50,
|
||||||
streamMode: ["updates", "messages", "custom"], subgraphs: true,
|
streamMode: ["updates", "messages", "custom"], subgraphs: true,
|
||||||
configurable: {
|
configurable: {
|
||||||
thread_id: 'default-session'
|
thread_id: userId
|
||||||
}
|
}
|
||||||
})) {
|
})) {
|
||||||
const isSubagent = namespace.some(s => s.startsWith("tools:"));
|
const isSubagent = namespace.some(s => s.startsWith("tools:"));
|
||||||
@@ -59,18 +85,17 @@ export default class EscortAdminAgent {
|
|||||||
// Subagent results returned to main agent
|
// Subagent results returned to main agent
|
||||||
for (const msg of data_.messages ?? []) {
|
for (const msg of data_.messages ?? []) {
|
||||||
if (msg.type === "tool") {
|
if (msg.type === "tool") {
|
||||||
console.log(`\nSubagent complete: ${msg.name}`);
|
logger.info(`Subagent complete: ${msg.name}, Result: ${String(msg.content).slice(0, 200)}`);
|
||||||
console.log(` Result: ${String(msg.content).slice(0, 200)}...`);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else if (nodeName === "model_request") {
|
} else if (nodeName === "model_request") {
|
||||||
this.messages.push(...data_.messages);
|
session.messages.push(...data_.messages);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// Subagent updates (non-empty namespace)
|
// Subagent updates (non-empty namespace)
|
||||||
for (const [nodeName, data_] of Object.entries(data)) {
|
for (const [nodeName, data_] of Object.entries(data)) {
|
||||||
console.log(` [${namespace[0]}] step: ${nodeName}`);
|
logger.info(`[${namespace[0]}] step: ${nodeName}`);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -91,29 +116,23 @@ export default class EscortAdminAgent {
|
|||||||
callback(source, "tool", message.text, message.id);
|
callback(source, "tool", message.text, message.id);
|
||||||
}
|
}
|
||||||
} else if (mode === "custom") {
|
} else if (mode === "custom") {
|
||||||
this.logger.info("custom: ", data);
|
logger.info("custom: ", data);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
_genAgent(userInfo) {
|
_genAgent(userInfo) {
|
||||||
if (this.agent) {
|
|
||||||
return this.agent;
|
|
||||||
}
|
|
||||||
|
|
||||||
this.messages = [];
|
|
||||||
|
|
||||||
const rootDir = process.cwd();
|
const rootDir = process.cwd();
|
||||||
const backend = new FilesystemBackend({ rootDir });
|
const backend = new FilesystemBackend({ rootDir });
|
||||||
|
|
||||||
this.flashModel = new ChatDeepSeek({
|
this.flashModel = new ChatDeepSeek({
|
||||||
model: 'deepseek-v4-flash',
|
model: config.agent.deepseek.flashModel,
|
||||||
apiKey: 'sk-a58ccd82b7ba4ce3ac176a88c9381095',
|
apiKey: config.agent.deepseek.apiKey,
|
||||||
temperature: 0.0
|
temperature: 0.0
|
||||||
});
|
});
|
||||||
this.proModel = new ChatDeepSeek({
|
this.proModel = new ChatDeepSeek({
|
||||||
model: 'deepseek-v4-pro',
|
model: config.agent.deepseek.proModel,
|
||||||
apiKey: 'sk-a58ccd82b7ba4ce3ac176a88c9381095',
|
apiKey: config.agent.deepseek.apiKey,
|
||||||
temperature: 0.3
|
temperature: 0.3
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -122,7 +141,7 @@ export default class EscortAdminAgent {
|
|||||||
description: "查询和设置陪诊预约记录",
|
description: "查询和设置陪诊预约记录",
|
||||||
systemPrompt: "根据用户指令,调用工具完成查询和设置陪诊记录。",
|
systemPrompt: "根据用户指令,调用工具完成查询和设置陪诊记录。",
|
||||||
model: this.flashModel,
|
model: this.flashModel,
|
||||||
tools: [escortRecordQueryTool],
|
tools: [escortRecordQueryTool, escortRecordSetTool],
|
||||||
};
|
};
|
||||||
|
|
||||||
const escortResearchSubagent = {
|
const escortResearchSubagent = {
|
||||||
@@ -133,17 +152,16 @@ export default class EscortAdminAgent {
|
|||||||
tools: [webFetchTool, webSearchTool],
|
tools: [webFetchTool, webSearchTool],
|
||||||
};
|
};
|
||||||
|
|
||||||
this.agent = createDeepAgent({
|
return createDeepAgent({
|
||||||
name: "deep-agent",
|
name: "deep-agent",
|
||||||
model: this.flashModel,
|
model: this.flashModel,
|
||||||
systemPrompt: EscortAdminPrompts.buildSystemPrompt(userInfo),
|
systemPrompt: EscortAdminPrompts.buildSystemPrompt(userInfo),
|
||||||
backend,
|
backend,
|
||||||
tools: [getEnvTool, webFetchTool, webSearchTool, getLatLngTool, httpGetTool, httpPostTool,
|
tools: [getEnvTool, webFetchTool, webSearchTool, getLatLngTool, httpGetTool, httpPostTool,
|
||||||
getCalendarInfoTool, getLunarCalendarInfoTool, getYearHolidaysTool, getYearTermsTool,
|
getCalendarInfoTool, getLunarCalendarInfoTool, getYearHolidaysTool, getYearTermsTool,
|
||||||
escortRecordQueryTool, escortRecordSetTool]
|
escortRecordQueryTool, escortRecordSetTool],
|
||||||
|
subagents: [escortRecordOperSubagent, escortResearchSubagent]
|
||||||
});
|
});
|
||||||
|
|
||||||
return this.agent;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -59,6 +59,19 @@ const escortRecordQueryTool = tool(
|
|||||||
.describe(
|
.describe(
|
||||||
"Appointment status: pending (待确认), confirmed (已确认), in_progress (进行中), completed (已完成), cancelled (已取消)"
|
"Appointment status: pending (待确认), confirmed (已确认), in_progress (进行中), completed (已完成), cancelled (已取消)"
|
||||||
),
|
),
|
||||||
|
page: z
|
||||||
|
.number()
|
||||||
|
.int()
|
||||||
|
.min(1)
|
||||||
|
.optional()
|
||||||
|
.describe("Page number, starting from 1"),
|
||||||
|
pageSize: z
|
||||||
|
.number()
|
||||||
|
.int()
|
||||||
|
.min(1)
|
||||||
|
.max(50)
|
||||||
|
.optional()
|
||||||
|
.describe("Records per page (1-50), default 20"),
|
||||||
}),
|
}),
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -13,21 +13,22 @@ export const getEnvTool = tool(
|
|||||||
async (input) => {
|
async (input) => {
|
||||||
const { name } = input || {};
|
const { name } = input || {};
|
||||||
|
|
||||||
|
const sensitiveKeys = ['API_KEY', 'SECRET', 'PASSWORD', 'TOKEN', 'CREDENTIAL'];
|
||||||
|
const isSensitive = (key) => sensitiveKeys.some(s => key.toUpperCase().includes(s));
|
||||||
|
|
||||||
if (name) {
|
if (name) {
|
||||||
const value = process.env[name];
|
const value = process.env[name];
|
||||||
if (value === undefined) {
|
if (value === undefined) {
|
||||||
return JSON.stringify({ error: `Environment variable '${name}' not found` });
|
return JSON.stringify({ error: `Environment variable '${name}' not found` });
|
||||||
}
|
}
|
||||||
return JSON.stringify({ name, value });
|
// 按名查询同样脱敏,防止 LLM 绕过读取密钥
|
||||||
|
return JSON.stringify({ name, value: isSensitive(name) ? '***REDACTED***' : value });
|
||||||
}
|
}
|
||||||
|
|
||||||
// 返回所有环境变量(过滤敏感信息)
|
// 返回所有环境变量(过滤敏感信息)
|
||||||
const env = { ...process.env };
|
const env = { ...process.env };
|
||||||
const sensitiveKeys = ['API_KEY', 'SECRET', 'PASSWORD', 'TOKEN', 'CREDENTIAL'];
|
|
||||||
|
|
||||||
for (const key of Object.keys(env)) {
|
for (const key of Object.keys(env)) {
|
||||||
const upperKey = key.toUpperCase();
|
if (isSensitive(key)) {
|
||||||
if (sensitiveKeys.some(s => upperKey.includes(s))) {
|
|
||||||
env[key] = '***REDACTED***';
|
env[key] = '***REDACTED***';
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,74 +0,0 @@
|
|||||||
import * as z from 'zod';
|
|
||||||
import { tool } from 'langchain';
|
|
||||||
import { spawn } from 'child_process';
|
|
||||||
|
|
||||||
export const winCmdTool = tool(
|
|
||||||
async ({ command, cwd, timeout = 30000 }) => {
|
|
||||||
const workingDir = cwd || process.cwd();
|
|
||||||
|
|
||||||
return new Promise((resolve) => {
|
|
||||||
let stdout = '';
|
|
||||||
let stderr = '';
|
|
||||||
let killed = false;
|
|
||||||
|
|
||||||
const proc = spawn('cmd.exe', ['/c', command], {
|
|
||||||
cwd: workingDir,
|
|
||||||
shell: false,
|
|
||||||
windowsHide: true
|
|
||||||
});
|
|
||||||
|
|
||||||
const timer = setTimeout(() => {
|
|
||||||
killed = true;
|
|
||||||
proc.kill('SIGKILL');
|
|
||||||
}, timeout);
|
|
||||||
|
|
||||||
proc.stdout.on('data', (data) => {
|
|
||||||
stdout += data.toString();
|
|
||||||
});
|
|
||||||
|
|
||||||
proc.stderr.on('data', (data) => {
|
|
||||||
stderr += data.toString();
|
|
||||||
});
|
|
||||||
|
|
||||||
proc.on('close', (code) => {
|
|
||||||
clearTimeout(timer);
|
|
||||||
if (killed) {
|
|
||||||
resolve(JSON.stringify({
|
|
||||||
success: false,
|
|
||||||
error: `Command timed out after ${timeout}ms`,
|
|
||||||
command,
|
|
||||||
cwd: workingDir
|
|
||||||
}));
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
resolve(JSON.stringify({
|
|
||||||
success: code === 0,
|
|
||||||
exitCode: code,
|
|
||||||
stdout: stdout.trim(),
|
|
||||||
stderr: stderr.trim(),
|
|
||||||
command,
|
|
||||||
cwd: workingDir
|
|
||||||
}));
|
|
||||||
});
|
|
||||||
|
|
||||||
proc.on('error', (error) => {
|
|
||||||
clearTimeout(timer);
|
|
||||||
resolve(JSON.stringify({
|
|
||||||
success: false,
|
|
||||||
error: error.message,
|
|
||||||
command,
|
|
||||||
cwd: workingDir
|
|
||||||
}));
|
|
||||||
});
|
|
||||||
});
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'win_cmd',
|
|
||||||
description: '在 Windows 系统上执行 cmd.exe 命令行命令。适用于运行 Windows 批处理命令、文件系统操作命令(如 dir、copy、del、mkdir 等)。不支持 PowerShell 命令。',
|
|
||||||
schema: z.object({
|
|
||||||
command: z.string().describe('要执行的 cmd.exe 命令或命令组合,使用 && 连接多个命令,如 "dir /b" 或 "echo hello && dir"'),
|
|
||||||
cwd: z.string().optional().describe('执行命令的工作目录,默认为当前工作目录'),
|
|
||||||
timeout: z.number().optional().describe('命令执行超时时间(毫秒),默认30000')
|
|
||||||
})
|
|
||||||
}
|
|
||||||
);
|
|
||||||
+10
-15
@@ -1,10 +1,12 @@
|
|||||||
import 'dotenv/config';
|
import 'dotenv/config';
|
||||||
import path from 'path';
|
import path from 'path';
|
||||||
import { createDeepAgent, FilesystemBackend, CompositeBackend, StoreBackend } from "deepagents";
|
import { createDeepAgent, FilesystemBackend, CompositeBackend } from "deepagents";
|
||||||
import { ChatOpenAI } from "@langchain/openai";
|
import { ChatOpenAI } from "@langchain/openai";
|
||||||
import { AIMessageChunk, ToolMessage, summarizationMiddleware } from "langchain";
|
import { AIMessageChunk, ToolMessage } from "langchain";
|
||||||
import { SystemMessage, HumanMessage, AIMessage } from "@langchain/core/messages";
|
import { HumanMessage } from "@langchain/core/messages";
|
||||||
import { ChatDeepSeek } from "@langchain/deepseek";
|
import { ChatDeepSeek } from "@langchain/deepseek";
|
||||||
|
import config from '../../conf.json' with { type: 'json' };
|
||||||
|
import logger from '../../utils/logger.js';
|
||||||
import Prompts from "./prompts.js";
|
import Prompts from "./prompts.js";
|
||||||
import {
|
import {
|
||||||
getEnvTool, webFetchTool, webSearchTool, getCalendarInfoTool,
|
getEnvTool, webFetchTool, webSearchTool, getCalendarInfoTool,
|
||||||
@@ -62,8 +64,7 @@ export default class EscortAgent {
|
|||||||
// Subagent results returned to main agent
|
// Subagent results returned to main agent
|
||||||
for (const msg of data_.messages ?? []) {
|
for (const msg of data_.messages ?? []) {
|
||||||
if (msg.type === "tool") {
|
if (msg.type === "tool") {
|
||||||
console.log(`\nSubagent complete: ${msg.name}`);
|
logger.info(`Subagent complete: ${msg.name}, Result: ${String(msg.content).slice(0, 200)}`);
|
||||||
console.log(` Result: ${String(msg.content).slice(0, 200)}...`);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else if (nodeName === "model_request") {
|
} else if (nodeName === "model_request") {
|
||||||
@@ -73,7 +74,7 @@ export default class EscortAgent {
|
|||||||
} else {
|
} else {
|
||||||
// Subagent updates (non-empty namespace)
|
// Subagent updates (non-empty namespace)
|
||||||
for (const [nodeName, data_] of Object.entries(data)) {
|
for (const [nodeName, data_] of Object.entries(data)) {
|
||||||
console.log(` [${namespace[0]}] step: ${nodeName}`);
|
logger.info(`[${namespace[0]}] step: ${nodeName}`);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -97,7 +98,7 @@ export default class EscortAgent {
|
|||||||
callback(source, "tool", message.text, message.id);
|
callback(source, "tool", message.text, message.id);
|
||||||
}
|
}
|
||||||
} else if (mode === "custom") {
|
} else if (mode === "custom") {
|
||||||
this.logger.info("custom: ", data);
|
logger.info("custom: ", data);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -113,7 +114,6 @@ export default class EscortAgent {
|
|||||||
|
|
||||||
if (userInfo) {
|
if (userInfo) {
|
||||||
const userMemoryPath = path.join(rootDir, "data", userInfo._id, "memories");
|
const userMemoryPath = path.join(rootDir, "data", userInfo._id, "memories");
|
||||||
console.log(userMemoryPath);
|
|
||||||
backend = new CompositeBackend(
|
backend = new CompositeBackend(
|
||||||
new FilesystemBackend({ rootDir }),
|
new FilesystemBackend({ rootDir }),
|
||||||
{
|
{
|
||||||
@@ -126,15 +126,10 @@ export default class EscortAgent {
|
|||||||
}
|
}
|
||||||
|
|
||||||
this.flashModel = new ChatDeepSeek({
|
this.flashModel = new ChatDeepSeek({
|
||||||
model: 'deepseek-v4-flash',
|
model: config.agent.deepseek.flashModel,
|
||||||
apiKey: 'sk-a58ccd82b7ba4ce3ac176a88c9381095',
|
apiKey: config.agent.deepseek.apiKey,
|
||||||
temperature: 0.0
|
temperature: 0.0
|
||||||
});
|
});
|
||||||
this.proModel = new ChatDeepSeek({
|
|
||||||
model: 'deepseek-v4-pro',
|
|
||||||
apiKey: 'sk-a58ccd82b7ba4ce3ac176a88c9381095',
|
|
||||||
temperature: 0.3
|
|
||||||
});
|
|
||||||
|
|
||||||
this.agent = createDeepAgent({
|
this.agent = createDeepAgent({
|
||||||
name: "deep-agent",
|
name: "deep-agent",
|
||||||
|
|||||||
@@ -66,6 +66,19 @@ function createEscortRecordQueryTool(userInfo) {
|
|||||||
.describe(
|
.describe(
|
||||||
"Appointment status: pending (待确认), confirmed (已确认), in_progress (进行中), completed (已完成), cancelled (已取消)"
|
"Appointment status: pending (待确认), confirmed (已确认), in_progress (进行中), completed (已完成), cancelled (已取消)"
|
||||||
),
|
),
|
||||||
|
page: z
|
||||||
|
.number()
|
||||||
|
.int()
|
||||||
|
.min(1)
|
||||||
|
.optional()
|
||||||
|
.describe("Page number, starting from 1"),
|
||||||
|
pageSize: z
|
||||||
|
.number()
|
||||||
|
.int()
|
||||||
|
.min(1)
|
||||||
|
.max(50)
|
||||||
|
.optional()
|
||||||
|
.describe("Records per page (1-50), default 20"),
|
||||||
}),
|
}),
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -13,21 +13,22 @@ export const getEnvTool = tool(
|
|||||||
async (input) => {
|
async (input) => {
|
||||||
const { name } = input || {};
|
const { name } = input || {};
|
||||||
|
|
||||||
|
const sensitiveKeys = ['API_KEY', 'SECRET', 'PASSWORD', 'TOKEN', 'CREDENTIAL'];
|
||||||
|
const isSensitive = (key) => sensitiveKeys.some(s => key.toUpperCase().includes(s));
|
||||||
|
|
||||||
if (name) {
|
if (name) {
|
||||||
const value = process.env[name];
|
const value = process.env[name];
|
||||||
if (value === undefined) {
|
if (value === undefined) {
|
||||||
return JSON.stringify({ error: `Environment variable '${name}' not found` });
|
return JSON.stringify({ error: `Environment variable '${name}' not found` });
|
||||||
}
|
}
|
||||||
return JSON.stringify({ name, value });
|
// 按名查询同样脱敏,防止 LLM 绕过读取密钥
|
||||||
|
return JSON.stringify({ name, value: isSensitive(name) ? '***REDACTED***' : value });
|
||||||
}
|
}
|
||||||
|
|
||||||
// 返回所有环境变量(过滤敏感信息)
|
// 返回所有环境变量(过滤敏感信息)
|
||||||
const env = { ...process.env };
|
const env = { ...process.env };
|
||||||
const sensitiveKeys = ['API_KEY', 'SECRET', 'PASSWORD', 'TOKEN', 'CREDENTIAL'];
|
|
||||||
|
|
||||||
for (const key of Object.keys(env)) {
|
for (const key of Object.keys(env)) {
|
||||||
const upperKey = key.toUpperCase();
|
if (isSensitive(key)) {
|
||||||
if (sensitiveKeys.some(s => upperKey.includes(s))) {
|
|
||||||
env[key] = '***REDACTED***';
|
env[key] = '***REDACTED***';
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,74 +0,0 @@
|
|||||||
import * as z from 'zod';
|
|
||||||
import { tool } from 'langchain';
|
|
||||||
import { spawn } from 'child_process';
|
|
||||||
|
|
||||||
export const winCmdTool = tool(
|
|
||||||
async ({ command, cwd, timeout = 30000 }) => {
|
|
||||||
const workingDir = cwd || process.cwd();
|
|
||||||
|
|
||||||
return new Promise((resolve) => {
|
|
||||||
let stdout = '';
|
|
||||||
let stderr = '';
|
|
||||||
let killed = false;
|
|
||||||
|
|
||||||
const proc = spawn('cmd.exe', ['/c', command], {
|
|
||||||
cwd: workingDir,
|
|
||||||
shell: false,
|
|
||||||
windowsHide: true
|
|
||||||
});
|
|
||||||
|
|
||||||
const timer = setTimeout(() => {
|
|
||||||
killed = true;
|
|
||||||
proc.kill('SIGKILL');
|
|
||||||
}, timeout);
|
|
||||||
|
|
||||||
proc.stdout.on('data', (data) => {
|
|
||||||
stdout += data.toString();
|
|
||||||
});
|
|
||||||
|
|
||||||
proc.stderr.on('data', (data) => {
|
|
||||||
stderr += data.toString();
|
|
||||||
});
|
|
||||||
|
|
||||||
proc.on('close', (code) => {
|
|
||||||
clearTimeout(timer);
|
|
||||||
if (killed) {
|
|
||||||
resolve(JSON.stringify({
|
|
||||||
success: false,
|
|
||||||
error: `Command timed out after ${timeout}ms`,
|
|
||||||
command,
|
|
||||||
cwd: workingDir
|
|
||||||
}));
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
resolve(JSON.stringify({
|
|
||||||
success: code === 0,
|
|
||||||
exitCode: code,
|
|
||||||
stdout: stdout.trim(),
|
|
||||||
stderr: stderr.trim(),
|
|
||||||
command,
|
|
||||||
cwd: workingDir
|
|
||||||
}));
|
|
||||||
});
|
|
||||||
|
|
||||||
proc.on('error', (error) => {
|
|
||||||
clearTimeout(timer);
|
|
||||||
resolve(JSON.stringify({
|
|
||||||
success: false,
|
|
||||||
error: error.message,
|
|
||||||
command,
|
|
||||||
cwd: workingDir
|
|
||||||
}));
|
|
||||||
});
|
|
||||||
});
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'win_cmd',
|
|
||||||
description: '在 Windows 系统上执行 cmd.exe 命令行命令。适用于运行 Windows 批处理命令、文件系统操作命令(如 dir、copy、del、mkdir 等)。不支持 PowerShell 命令。',
|
|
||||||
schema: z.object({
|
|
||||||
command: z.string().describe('要执行的 cmd.exe 命令或命令组合,使用 && 连接多个命令,如 "dir /b" 或 "echo hello && dir"'),
|
|
||||||
cwd: z.string().optional().describe('执行命令的工作目录,默认为当前工作目录'),
|
|
||||||
timeout: z.number().optional().describe('命令执行超时时间(毫秒),默认30000')
|
|
||||||
})
|
|
||||||
}
|
|
||||||
);
|
|
||||||
@@ -5,18 +5,31 @@
|
|||||||
"wxapp-escort-admin": {
|
"wxapp-escort-admin": {
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"agent": {
|
||||||
|
"deepseek": {
|
||||||
|
"apiKey": "sk-a58ccd82b7ba4ce3ac176a88c9381095",
|
||||||
|
"flashModel": "deepseek-v4-flash",
|
||||||
|
"proModel": "deepseek-v4-pro"
|
||||||
|
},
|
||||||
|
"glm": {
|
||||||
|
"apiKey": "ed5f2ec42cb9413f87402d38321553f8.u0FalmfLdXxmst69",
|
||||||
|
"flashModel": "glm-5.3-flash",
|
||||||
|
"proModel": "glm-5.3"
|
||||||
|
}
|
||||||
|
},
|
||||||
"mongodb": {
|
"mongodb": {
|
||||||
"str": "mongodb://huashengtec.com:6000",
|
"str": "mongodb://ehason:Ehason_dbuser_2026@huashengtec.com:6000/?authSource=admin&appName=ws-health&retryWrites=true&w=majority",
|
||||||
"host": "huashengtec.com",
|
"host": "huashengtec.com",
|
||||||
"db": "health",
|
|
||||||
"option": {
|
"option": {
|
||||||
"user": "ehason",
|
|
||||||
"pass": "Ehason_dbuser_2026",
|
|
||||||
"dbName": "eiot_health",
|
"dbName": "eiot_health",
|
||||||
"authSource": "admin",
|
|
||||||
"autoIndex": true,
|
"autoIndex": true,
|
||||||
"socketTimeoutMS": 3000,
|
"maxPoolSize": 50,
|
||||||
"serverSelectionTimeoutMS": 30000
|
"minPoolSize": 5,
|
||||||
|
"maxIdleTimeMS": 60000,
|
||||||
|
"connectTimeoutMS": 10000,
|
||||||
|
"serverSelectionTimeoutMS": 30000,
|
||||||
|
"socketTimeoutMS": 45000,
|
||||||
|
"compressors": "zlib"
|
||||||
},
|
},
|
||||||
"debug": true
|
"debug": true
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,6 +3,8 @@
|
|||||||
import mongoose from 'mongoose';
|
import mongoose from 'mongoose';
|
||||||
import { EscortRecordSchema } from "./schema/escort_record.js"
|
import { EscortRecordSchema } from "./schema/escort_record.js"
|
||||||
import { HealthProfileSchema } from "./schema/health_profile.js"
|
import { HealthProfileSchema } from "./schema/health_profile.js"
|
||||||
|
import { OrganizationSchema } from "./schema/organization.js"
|
||||||
|
import { EmployeeSchema } from "./schema/employee.js"
|
||||||
import config from '../conf.json' with { type: 'json' };
|
import config from '../conf.json' with { type: 'json' };
|
||||||
import logger from '../utils/logger.js';
|
import logger from '../utils/logger.js';
|
||||||
|
|
||||||
@@ -12,6 +14,7 @@ class MongoDBSchema {
|
|||||||
this.User = null;
|
this.User = null;
|
||||||
this.EscortRecord = null;
|
this.EscortRecord = null;
|
||||||
this.Organization = null;
|
this.Organization = null;
|
||||||
|
this.Employee = null;
|
||||||
this.HealthProfile = null;
|
this.HealthProfile = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -40,6 +43,8 @@ class MongoDBSchema {
|
|||||||
|
|
||||||
this.EscortRecord = this.dbConnection.model('escort_record', EscortRecordSchema)
|
this.EscortRecord = this.dbConnection.model('escort_record', EscortRecordSchema)
|
||||||
this.HealthProfile = this.dbConnection.model('health_profile', HealthProfileSchema)
|
this.HealthProfile = this.dbConnection.model('health_profile', HealthProfileSchema)
|
||||||
|
this.Organization = this.dbConnection.model('organization', OrganizationSchema)
|
||||||
|
this.Employee = this.dbConnection.model('employee', EmployeeSchema)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user