feat: 新增工具插件、系统日志、workflow配置及前端优化

1. 新增工具插件(edit_file, python_executor, search_file, shell_executor, write_file)
2. 新增系统事件日志模块和API
3. 新增workflow配置文件和详情API
4. 前端增加SSE、错误边界、设置引导等组件
5. 优化认证加密、速率限制、配置加载等工具模块
6. 删除废弃的cluster和health API
7. 补充单元测试和集成测试

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-06-03 07:34:43 +00:00
parent f04fef916f
commit a53ffebe0e
57 changed files with 2804 additions and 271 deletions
@@ -18,7 +18,7 @@ from typing import List, Literal, Dict
class ApprovalToolData(BaseToolData):
"""``approval`` 工具的元数据:默认面向 control/consciousness 两类节点开放"""
"""``approval`` 工具的元数据:分配给所有系统节点和 skill_individual"""
is_system: bool = True
action_scope: List[
@@ -29,7 +29,7 @@ class ApprovalToolData(BaseToolData):
"growth_node",
"",
]
] = ["control_node", "consciousness_node"]
] = []
config_args: Dict[str, str] = {}
category: str = "system"
+2
View File
@@ -34,3 +34,5 @@ class BaseToolData(BaseModel):
config_args: Dict[str, str] = {}
category: str = "other"
"""工具分类:system(系统内置)、search(搜索)、mcp(MCP 服务器)、other(其他)"""
toolset: str = "other"
"""工具集:retrieval(检索)、generation(生成)、other(其他)。system_node 只能用 retrieval 集。"""
@@ -0,0 +1,54 @@
import os
from typing import List, Literal, Dict
from kilostar.plugin.tool_plugin.base_tool import BaseToolData
class EditFileToolData(BaseToolData):
is_system: bool = True
action_scope: List[
Literal[
"control_node",
"consciousness_node",
"regulatory_node",
"growth_node",
"",
]
] = []
config_args: Dict[str, str] = {}
category: str = "system"
async def edit_file(
file_path: str,
old_content: str,
new_content: str,
) -> str:
"""通过查找替换的方式编辑文件内容。
Args:
file_path: 文件的路径
old_content: 要被替换的原始内容片段
new_content: 替换后的新内容
Returns:
操作结果描述
"""
try:
if not os.path.exists(file_path):
return f"[Error] 文件不存在: {file_path}"
with open(file_path, "r", encoding="utf-8") as f:
content = f.read()
if old_content not in content:
return f"[Error] 未在文件中找到要替换的内容片段"
new_file_content = content.replace(old_content, new_content, 1)
with open(file_path, "w", encoding="utf-8") as f:
f.write(new_file_content)
return f"已成功编辑文件: {file_path}"
except Exception as e:
return f"[Error] 编辑文件失败: {e}"
@@ -33,7 +33,7 @@ class FileReaderToolData(BaseToolData):
"growth_node",
"",
]
] = ["control_node"]
] = []
config_args: Dict[str, str] = {}
category: str = "system"
@@ -0,0 +1,67 @@
import asyncio
import sys
import tempfile
import os
from typing import List, Literal, Dict
from kilostar.plugin.tool_plugin.base_tool import BaseToolData
class PythonExecutorToolData(BaseToolData):
is_system: bool = True
action_scope: List[
Literal[
"control_node",
"consciousness_node",
"regulatory_node",
"growth_node",
"",
]
] = []
config_args: Dict[str, str] = {}
category: str = "system"
async def python_executor(code: str, timeout: int = 30) -> str:
"""执行 Python 代码片段并返回输出。
Args:
code: 要执行的 Python 代码
timeout: 超时秒数,默认 30 秒
Returns:
代码的标准输出 + 标准错误
"""
tmp_file = None
try:
with tempfile.NamedTemporaryFile(
mode="w", suffix=".py", delete=False, encoding="utf-8"
) as f:
f.write(code)
tmp_file = f.name
proc = await asyncio.create_subprocess_exec(
sys.executable, tmp_file,
stdout=asyncio.subprocess.PIPE,
stderr=asyncio.subprocess.PIPE,
)
stdout, stderr = await asyncio.wait_for(
proc.communicate(), timeout=timeout
)
output = stdout.decode("utf-8", errors="replace")
err_output = stderr.decode("utf-8", errors="replace")
result = ""
if output:
result += output
if err_output:
result += f"\n[stderr]\n{err_output}"
if proc.returncode != 0:
result += f"\n[exit code: {proc.returncode}]"
return result.strip() or "(no output)"
except asyncio.TimeoutError:
return f"[Error] Python 代码执行超时({timeout}s"
except Exception as e:
return f"[Error] 执行失败: {e}"
finally:
if tmp_file and os.path.exists(tmp_file):
os.unlink(tmp_file)
@@ -0,0 +1,58 @@
import asyncio
from typing import List, Literal, Dict
from kilostar.plugin.tool_plugin.base_tool import BaseToolData
class SearchFileToolData(BaseToolData):
is_system: bool = True
action_scope: List[
Literal[
"control_node",
"consciousness_node",
"regulatory_node",
"growth_node",
"",
]
] = []
config_args: Dict[str, str] = {}
category: str = "system"
async def search_file(
keyword: str,
directory: str = ".",
file_pattern: str = "*",
max_results: int = 20,
) -> str:
"""在指定目录下递归搜索包含关键字的文件内容。
Args:
keyword: 要搜索的关键字或正则表达式
directory: 搜索的根目录,默认当前目录
file_pattern: 文件名匹配模式,如 "*.py"
max_results: 最大返回结果数
Returns:
匹配的文件名和行内容
"""
try:
cmd = (
f"grep -rn --include='{file_pattern}' "
f"-m {max_results} '{keyword}' '{directory}' 2>/dev/null "
f"| head -n {max_results}"
)
proc = await asyncio.create_subprocess_shell(
cmd,
stdout=asyncio.subprocess.PIPE,
stderr=asyncio.subprocess.PIPE,
)
stdout, _ = await asyncio.wait_for(proc.communicate(), timeout=30)
output = stdout.decode("utf-8", errors="replace").strip()
if not output:
return f"未找到包含 '{keyword}' 的匹配项"
return output
except asyncio.TimeoutError:
return "[Error] 搜索超时"
except Exception as e:
return f"[Error] 搜索失败: {e}"
@@ -0,0 +1,54 @@
import asyncio
from typing import List, Literal, Dict
from kilostar.plugin.tool_plugin.base_tool import BaseToolData
class ShellExecutorToolData(BaseToolData):
is_system: bool = True
action_scope: List[
Literal[
"control_node",
"consciousness_node",
"regulatory_node",
"growth_node",
"",
]
] = []
config_args: Dict[str, str] = {}
category: str = "system"
async def shell_executor(command: str, timeout: int = 30) -> str:
"""在服务器上执行 shell 命令并返回输出。
Args:
command: 要执行的 shell 命令
timeout: 超时秒数,默认 30 秒
Returns:
命令的 stdout + stderr 输出
"""
try:
proc = await asyncio.create_subprocess_shell(
command,
stdout=asyncio.subprocess.PIPE,
stderr=asyncio.subprocess.PIPE,
)
stdout, stderr = await asyncio.wait_for(
proc.communicate(), timeout=timeout
)
output = stdout.decode("utf-8", errors="replace")
err_output = stderr.decode("utf-8", errors="replace")
result = ""
if output:
result += output
if err_output:
result += f"\n[stderr]\n{err_output}"
if proc.returncode != 0:
result += f"\n[exit code: {proc.returncode}]"
return result.strip() or "(no output)"
except asyncio.TimeoutError:
return f"[Error] 命令执行超时({timeout}s"
except Exception as e:
return f"[Error] 执行失败: {e}"
@@ -0,0 +1,42 @@
import os
from typing import List, Literal, Dict
from kilostar.plugin.tool_plugin.base_tool import BaseToolData
class WriteFileToolData(BaseToolData):
is_system: bool = True
action_scope: List[
Literal[
"control_node",
"consciousness_node",
"regulatory_node",
"growth_node",
"",
]
] = []
config_args: Dict[str, str] = {}
category: str = "system"
async def write_file(file_path: str, content: str) -> str:
"""将内容写入指定文件(会覆盖已有内容,自动创建目录)。
Args:
file_path: 文件的路径
content: 要写入的内容
Returns:
操作结果描述
"""
try:
dir_path = os.path.dirname(file_path)
if dir_path:
os.makedirs(dir_path, exist_ok=True)
with open(file_path, "w", encoding="utf-8") as f:
f.write(content)
return f"已成功写入文件: {file_path}{len(content)} 字符)"
except Exception as e:
return f"[Error] 写入文件失败: {e}"