mirror of
https://gitee.com/mateos/mateclaw.git
synced 2026-09-13 11:13:43 +08:00
feat(db): introduce Flyway migration framework and unify H2/MySQL schemas
- Add Flyway baseline migration (V1) for both H2 and MySQL - Remove 5 legacy SchemaMigration ApplicationRunner classes - Add WorkspacePathGuard for file tool sandbox enforcement - Inject workspace basePath context into agent graph state and tool executor - Add workspace basePath config UI in frontend - Fix workspace slug preservation on update
This commit is contained in:
parent
f98f4d68b9
commit
cc28f665f1
@ -1 +0,0 @@
|
||||
Hello World
|
||||
@ -267,6 +267,16 @@
|
||||
<version>2.2.0</version>
|
||||
</dependency>
|
||||
|
||||
<!-- ===== Database Migration (Flyway) ===== -->
|
||||
<dependency>
|
||||
<groupId>org.flywaydb</groupId>
|
||||
<artifactId>flyway-core</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.flywaydb</groupId>
|
||||
<artifactId>flyway-mysql</artifactId>
|
||||
</dependency>
|
||||
|
||||
<!-- ===== Spring Boot Test ===== -->
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
|
||||
@ -120,6 +120,7 @@ public class AgentGraphBuilder {
|
||||
private final vip.mate.agent.context.ConversationWindowManager conversationWindowManager;
|
||||
private final vip.mate.llm.chatgpt.ChatGPTResponsesClient chatGPTResponsesClient;
|
||||
private final WikiContextService wikiContextService;
|
||||
private final vip.mate.workspace.core.service.WorkspaceService workspaceService;
|
||||
|
||||
/**
|
||||
* 根据 AgentEntity 构建完整的 Agent 实例
|
||||
@ -202,6 +203,19 @@ public class AgentGraphBuilder {
|
||||
agent.topP = runtimeModel.getTopP();
|
||||
agent.toolCallingEnabled = toolCallingEnabled;
|
||||
|
||||
// 查找工作区活动目录
|
||||
if (entity.getWorkspaceId() != null) {
|
||||
try {
|
||||
var workspace = workspaceService.getById(entity.getWorkspaceId());
|
||||
if (workspace != null && workspace.getBasePath() != null && !workspace.getBasePath().isBlank()) {
|
||||
agent.workspaceBasePath = workspace.getBasePath();
|
||||
log.info("Agent {} bound to workspace basePath: {}", entity.getName(), agent.workspaceBasePath);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
log.warn("Failed to lookup workspace basePath for agent {}: {}", entity.getName(), e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
log.info("Built agent instance: {} (type={}, protocol={}, tools={}, toolCallingEnabled={})",
|
||||
entity.getName(), entity.getAgentType(), protocol.getId(),
|
||||
toolSet.size(), agent.toolCallingEnabled);
|
||||
|
||||
@ -46,6 +46,9 @@ public abstract class BaseAgent {
|
||||
/** 最大工具调用迭代次数 */
|
||||
protected int maxIterations = 25;
|
||||
|
||||
/** 工作区活动目录(限制文件工具访问范围,为空不限制) */
|
||||
protected String workspaceBasePath;
|
||||
|
||||
/** 模型名称 */
|
||||
protected String modelName;
|
||||
|
||||
|
||||
@ -27,4 +27,16 @@ public final class RuntimeContextInjector {
|
||||
return "[system-context] 当前时间: " + now.format(DATE_FMT)
|
||||
+ " " + now.format(TIME_FMT) + " (Asia/Shanghai)";
|
||||
}
|
||||
|
||||
/**
|
||||
* 构建运行时上下文消息,包含当前日期、时间和工作目录。
|
||||
*/
|
||||
public static String buildContextMessage(String workspaceBasePath) {
|
||||
StringBuilder sb = new StringBuilder(buildContextMessage());
|
||||
if (workspaceBasePath != null && !workspaceBasePath.isBlank()) {
|
||||
sb.append("\n[system-context] 工作目录: ").append(workspaceBasePath)
|
||||
.append("\n你只能在此目录及其子目录内读写文件和执行命令。");
|
||||
}
|
||||
return sb.toString();
|
||||
}
|
||||
}
|
||||
|
||||
@ -362,6 +362,7 @@ public class StateGraphReActAgent extends BaseAgent implements StructuredStreamC
|
||||
inputs.put(USER_MESSAGE, userMessage);
|
||||
inputs.put(CONVERSATION_ID, conversationId);
|
||||
inputs.put(AGENT_ID, agentId != null ? agentId : "");
|
||||
inputs.put(WORKSPACE_BASE_PATH, workspaceBasePath != null ? workspaceBasePath : "");
|
||||
inputs.put(SYSTEM_PROMPT, systemPrompt != null ? systemPrompt : "你是一个有帮助的AI助手。");
|
||||
inputs.put(MESSAGES, messages);
|
||||
// 迭代控制
|
||||
|
||||
@ -146,11 +146,21 @@ public class ToolExecutionExecutor {
|
||||
|
||||
/** 当前执行的 requesterId,传递给 ToolExecutionContext */
|
||||
private volatile String currentRequesterId;
|
||||
/** 当前工作区活动目录(为空不限制),传递给 ToolExecutionContext */
|
||||
private volatile String currentWorkspaceBasePath;
|
||||
|
||||
public ToolExecutionResult execute(List<AssistantMessage.ToolCall> toolCalls,
|
||||
String conversationId, String agentId,
|
||||
boolean isReplay, String requesterId) {
|
||||
return execute(toolCalls, conversationId, agentId, isReplay, requesterId, null);
|
||||
}
|
||||
|
||||
public ToolExecutionResult execute(List<AssistantMessage.ToolCall> toolCalls,
|
||||
String conversationId, String agentId,
|
||||
boolean isReplay, String requesterId,
|
||||
String workspaceBasePath) {
|
||||
this.currentRequesterId = requesterId;
|
||||
this.currentWorkspaceBasePath = workspaceBasePath;
|
||||
List<ToolResponseMessage.ToolResponse> allResponses = new ArrayList<>();
|
||||
List<GraphEventPublisher.GraphEvent> events = Collections.synchronizedList(new ArrayList<>());
|
||||
|
||||
@ -228,7 +238,8 @@ public class ToolExecutionExecutor {
|
||||
|
||||
// 4. 分类: concurrencySafe
|
||||
boolean safe = isConcurrencySafe(toolName);
|
||||
preparedCalls.add(new PreparedToolCall(toolCall, callback, arguments, safe, allResponses.size(), conversationId));
|
||||
preparedCalls.add(new PreparedToolCall(toolCall, callback, arguments, safe, allResponses.size(),
|
||||
conversationId, currentRequesterId, currentWorkspaceBasePath));
|
||||
// 占位,Phase 2 填充
|
||||
allResponses.add(null);
|
||||
}
|
||||
@ -392,8 +403,8 @@ public class ToolExecutionExecutor {
|
||||
toolName, pc.arguments != null && pc.arguments.length() > 200
|
||||
? pc.arguments.substring(0, 200) + "..." : pc.arguments);
|
||||
|
||||
// 注入工具执行上下文(供 VideoGenerateTool 等获取 conversationId / username)
|
||||
ToolExecutionContext.set(pc.conversationId, currentRequesterId);
|
||||
// 注入工具执行上下文(供 VideoGenerateTool 等获取 conversationId / username / workspaceBasePath)
|
||||
ToolExecutionContext.set(pc.conversationId, pc.requesterId, pc.workspaceBasePath);
|
||||
String result;
|
||||
try {
|
||||
result = pc.callback.call(pc.arguments);
|
||||
@ -530,7 +541,9 @@ public class ToolExecutionExecutor {
|
||||
String arguments,
|
||||
boolean concurrencySafe,
|
||||
int resultIndex,
|
||||
String conversationId
|
||||
String conversationId,
|
||||
String requesterId,
|
||||
String workspaceBasePath
|
||||
) {}
|
||||
|
||||
private record ApprovalBarrier(String pendingId, String toolName) {}
|
||||
|
||||
@ -62,9 +62,12 @@ public class ActionNode implements NodeAction {
|
||||
// 请求者身份(用于审批记录)
|
||||
String requesterId = accessor.requesterId();
|
||||
|
||||
// 获取工作区活动目录
|
||||
String workspaceBasePath = state.value(MateClawStateKeys.WORKSPACE_BASE_PATH, "");
|
||||
|
||||
// 委托 ToolExecutionExecutor 执行(两阶段:顺序 Guard + 分段并发执行)
|
||||
ToolExecutionExecutor.ToolExecutionResult result = executor.execute(
|
||||
toolCalls, conversationId, agentId, isReplay, requesterId);
|
||||
toolCalls, conversationId, agentId, isReplay, requesterId, workspaceBasePath);
|
||||
|
||||
ToolResponseMessage toolResponseMessage = ToolResponseMessage.builder()
|
||||
.responses(result.responses())
|
||||
|
||||
@ -171,9 +171,10 @@ public class ReasoningNode implements NodeAction {
|
||||
messages = trimmed;
|
||||
}
|
||||
|
||||
String workspaceBasePath = state.value(vip.mate.agent.graph.state.MateClawStateKeys.WORKSPACE_BASE_PATH, "");
|
||||
List<Message> promptMessages = new ArrayList<>();
|
||||
promptMessages.add(new SystemMessage(systemPrompt));
|
||||
promptMessages.add(new UserMessage(RuntimeContextInjector.buildContextMessage()));
|
||||
promptMessages.add(new UserMessage(RuntimeContextInjector.buildContextMessage(workspaceBasePath)));
|
||||
promptMessages.addAll(messages);
|
||||
|
||||
ChatOptions options;
|
||||
@ -222,7 +223,7 @@ public class ReasoningNode implements NodeAction {
|
||||
if (compactedMessages != null && compactedMessages.size() < messages.size()) {
|
||||
List<Message> retryPromptMessages = new ArrayList<>();
|
||||
retryPromptMessages.add(new SystemMessage(systemPrompt));
|
||||
retryPromptMessages.add(new UserMessage(RuntimeContextInjector.buildContextMessage()));
|
||||
retryPromptMessages.add(new UserMessage(RuntimeContextInjector.buildContextMessage(workspaceBasePath)));
|
||||
retryPromptMessages.addAll(compactedMessages);
|
||||
Prompt retryPrompt = new Prompt(retryPromptMessages, options);
|
||||
log.info("[ReasoningNode] Retrying with compacted messages: {} -> {} messages",
|
||||
|
||||
@ -269,6 +269,7 @@ public class StateGraphPlanExecuteAgent extends BaseAgent implements StructuredS
|
||||
systemPrompt != null ? systemPrompt : "你是一个有帮助的AI助手。");
|
||||
inputs.put(MateClawStateKeys.CONVERSATION_ID, conversationId);
|
||||
inputs.put(MateClawStateKeys.AGENT_ID, agentId != null ? agentId : "");
|
||||
inputs.put(MateClawStateKeys.WORKSPACE_BASE_PATH, workspaceBasePath != null ? workspaceBasePath : "");
|
||||
// 注入会话消息(复用 MateClawStateKeys.MESSAGES,与 ReAct 一致)
|
||||
inputs.put(MateClawStateKeys.MESSAGES, messages);
|
||||
// 注入 working context
|
||||
|
||||
@ -133,8 +133,9 @@ public class PlanGenerationNode implements NodeAction {
|
||||
// 拼接后会稀释 PLANNING_PROMPT 的指令优先级)
|
||||
List<Message> promptMessages = new ArrayList<>();
|
||||
promptMessages.add(new SystemMessage(PLANNING_PROMPT));
|
||||
// 注入运行时上下文(当前时间)
|
||||
promptMessages.add(new UserMessage(RuntimeContextInjector.buildContextMessage()));
|
||||
// 注入运行时上下文(当前时间 + 工作目录)
|
||||
String workspaceBasePath = state.value(MateClawStateKeys.WORKSPACE_BASE_PATH, "");
|
||||
promptMessages.add(new UserMessage(RuntimeContextInjector.buildContextMessage(workspaceBasePath)));
|
||||
|
||||
// 注入可用工具名称,帮助 LLM 判断用户目标是否需要工具
|
||||
if (toolSet != null && !toolSet.callbacks().isEmpty()) {
|
||||
|
||||
@ -85,6 +85,7 @@ public class StepExecutionNode implements NodeAction {
|
||||
|
||||
String conversationId = state.value(MateClawStateKeys.CONVERSATION_ID, "");
|
||||
String agentId = state.value(MateClawStateKeys.AGENT_ID, "");
|
||||
String workspaceBasePath = state.value(MateClawStateKeys.WORKSPACE_BASE_PATH, "");
|
||||
|
||||
if (stepIndex >= steps.size()) {
|
||||
log.warn("[StepExecution] stepIndex {} >= steps.size() {}, skipping", stepIndex, steps.size());
|
||||
@ -105,7 +106,7 @@ public class StepExecutionNode implements NodeAction {
|
||||
planningService.updateSubPlanStatus(planId, stepIndex, "running");
|
||||
|
||||
// 构建消息列表
|
||||
List<Message> messages = buildStepMessages(accessor, step, systemPrompt);
|
||||
List<Message> messages = buildStepMessages(accessor, step, systemPrompt, workspaceBasePath);
|
||||
|
||||
// 显式工具执行循环
|
||||
String finalResult = null;
|
||||
@ -186,7 +187,7 @@ public class StepExecutionNode implements NodeAction {
|
||||
} else {
|
||||
// 非预批准工具走正常执行器
|
||||
ToolExecutionExecutor.ToolExecutionResult execResult = executor.execute(
|
||||
List.of(toolCall), conversationId, agentId, false);
|
||||
List.of(toolCall), conversationId, agentId, false, "", workspaceBasePath);
|
||||
toolResponses.addAll(execResult.responses());
|
||||
events.addAll(execResult.events());
|
||||
if (execResult.awaitingApproval()) {
|
||||
@ -199,7 +200,7 @@ public class StepExecutionNode implements NodeAction {
|
||||
} else {
|
||||
// 正常路径:委托 ToolExecutionExecutor(支持并发执行 + 审批 barrier)
|
||||
ToolExecutionExecutor.ToolExecutionResult execResult = executor.execute(
|
||||
allToolCalls, conversationId, agentId, false);
|
||||
allToolCalls, conversationId, agentId, false, "", workspaceBasePath);
|
||||
toolResponses.addAll(execResult.responses());
|
||||
events.addAll(execResult.events());
|
||||
if (execResult.awaitingApproval()) {
|
||||
@ -286,7 +287,7 @@ public class StepExecutionNode implements NodeAction {
|
||||
.build();
|
||||
}
|
||||
|
||||
private List<Message> buildStepMessages(PlanStateAccessor accessor, String step, String systemPrompt) {
|
||||
private List<Message> buildStepMessages(PlanStateAccessor accessor, String step, String systemPrompt, String workspaceBasePath) {
|
||||
List<Message> messages = new ArrayList<>();
|
||||
|
||||
// Layer 1: System prompt(增强指令)
|
||||
@ -305,8 +306,8 @@ public class StepExecutionNode implements NodeAction {
|
||||
8. 每一步最多做一个必要的检查和一个必要的执行,不要无意义循环。
|
||||
""";
|
||||
messages.add(new SystemMessage(enhancedSystemPrompt));
|
||||
// 注入运行时上下文(当前时间)
|
||||
messages.add(new UserMessage(RuntimeContextInjector.buildContextMessage()));
|
||||
// 注入运行时上下文(当前时间 + 工作目录)
|
||||
messages.add(new UserMessage(RuntimeContextInjector.buildContextMessage(workspaceBasePath)));
|
||||
|
||||
// Layer 2: Working context(对话历史 + 步骤结果的受控长度摘要)
|
||||
String workingContext = accessor.workingContext();
|
||||
|
||||
@ -20,6 +20,8 @@ public final class MateClawStateKeys {
|
||||
public static final String CONVERSATION_ID = "conversation_id";
|
||||
public static final String SYSTEM_PROMPT = "system_prompt";
|
||||
public static final String AGENT_ID = "agent_id";
|
||||
/** 工作区活动目录(为空不限制文件访问范围) */
|
||||
public static final String WORKSPACE_BASE_PATH = "workspace_base_path";
|
||||
|
||||
// ===== 消息列表(APPEND 策略)=====
|
||||
public static final String MESSAGES = "messages";
|
||||
|
||||
@ -30,7 +30,7 @@ import java.util.Map;
|
||||
*/
|
||||
@Slf4j
|
||||
@Service
|
||||
@Order(55) // 在 ApprovalSchemaMigration(50) 之后
|
||||
@Order(55) // Schema 由 Flyway 管理,在 Flyway 迁移完成后执行
|
||||
@RequiredArgsConstructor
|
||||
public class ApprovalWorkflowService implements ApplicationRunner {
|
||||
|
||||
|
||||
@ -1,34 +0,0 @@
|
||||
package vip.mate.approval.config;
|
||||
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.boot.ApplicationArguments;
|
||||
import org.springframework.boot.ApplicationRunner;
|
||||
import org.springframework.core.annotation.Order;
|
||||
import org.springframework.jdbc.core.JdbcTemplate;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
/**
|
||||
* 审批表增量 Schema 迁移
|
||||
* <p>
|
||||
* 基础表 mate_tool_approval 已在 schema.sql / schema-mysql.sql 中定义。
|
||||
* 本类仅负责增量字段/索引迁移,确保从旧版本升级时自动补齐。
|
||||
* <p>
|
||||
* 当前无需增量迁移,保留框架以备未来扩展。
|
||||
*
|
||||
* @author MateClaw Team
|
||||
*/
|
||||
@Slf4j
|
||||
@Component
|
||||
@Order(50)
|
||||
@RequiredArgsConstructor
|
||||
public class ApprovalSchemaMigration implements ApplicationRunner {
|
||||
|
||||
private final JdbcTemplate jdbcTemplate;
|
||||
|
||||
@Override
|
||||
public void run(ApplicationArguments args) {
|
||||
// 当前无增量迁移。未来新增字段时在此添加 safeAddColumn 调用。
|
||||
log.debug("[ApprovalSchemaMigration] Incremental migration completed (no-op)");
|
||||
}
|
||||
}
|
||||
@ -68,7 +68,7 @@ public class DatabaseBootstrapRunner implements ApplicationRunner {
|
||||
|
||||
@Override
|
||||
public void run(ApplicationArguments args) throws Exception {
|
||||
runSchemaScript();
|
||||
// Schema creation is now handled by Flyway (db/migration/)
|
||||
runToolSyncScript();
|
||||
|
||||
if (isDataAlreadySeeded()) {
|
||||
@ -168,10 +168,6 @@ public class DatabaseBootstrapRunner implements ApplicationRunner {
|
||||
return isMySQL;
|
||||
}
|
||||
|
||||
private void runSchemaScript() {
|
||||
runScript(isMySQL() ? "db/schema-mysql.sql" : "db/schema.sql");
|
||||
}
|
||||
|
||||
private void runToolSyncScript() {
|
||||
String script = isMySQL() ? "db/tools-sync-mysql.sql" : "db/tools-sync.sql";
|
||||
runScript(script);
|
||||
|
||||
@ -1,40 +0,0 @@
|
||||
package vip.mate.cron.config;
|
||||
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.boot.ApplicationArguments;
|
||||
import org.springframework.boot.ApplicationRunner;
|
||||
import org.springframework.core.annotation.Order;
|
||||
import org.springframework.jdbc.core.JdbcTemplate;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
/**
|
||||
* CronJob 表 Schema 迁移
|
||||
*
|
||||
* @author MateClaw Team
|
||||
*/
|
||||
@Slf4j
|
||||
@Component
|
||||
@Order(200)
|
||||
@RequiredArgsConstructor
|
||||
public class CronSchemaMigration implements ApplicationRunner {
|
||||
|
||||
private final JdbcTemplate jdbcTemplate;
|
||||
|
||||
@Override
|
||||
public void run(ApplicationArguments args) {
|
||||
addColumnIfMissing("mate_cron_job", "timezone", "VARCHAR(64) NOT NULL DEFAULT 'Asia/Shanghai'");
|
||||
addColumnIfMissing("mate_cron_job", "task_type", "VARCHAR(16) NOT NULL DEFAULT 'text'");
|
||||
addColumnIfMissing("mate_cron_job", "request_body", "TEXT");
|
||||
addColumnIfMissing("mate_cron_job", "next_run_time", "DATETIME");
|
||||
log.info("[CronSchemaMigration] mate_cron_job schema migration completed");
|
||||
}
|
||||
|
||||
private void addColumnIfMissing(String table, String column, String definition) {
|
||||
try {
|
||||
jdbcTemplate.execute("ALTER TABLE " + table + " ADD COLUMN IF NOT EXISTS " + column + " " + definition);
|
||||
} catch (Exception e) {
|
||||
log.debug("[CronSchemaMigration] Column {} may already exist: {}", column, e.getMessage());
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -60,7 +60,7 @@ public class CronJobService implements ApplicationRunner {
|
||||
// ==================== 初始化与销毁 ====================
|
||||
|
||||
/**
|
||||
* 实现 ApplicationRunner,确保在 CronSchemaMigration(@Order=200) 执行完毕后再加载任务
|
||||
* 实现 ApplicationRunner,确保在 Flyway 迁移和 DatabaseBootstrapRunner 完成后再加载任务
|
||||
*/
|
||||
@Override
|
||||
public void run(ApplicationArguments args) {
|
||||
|
||||
@ -1,60 +0,0 @@
|
||||
package vip.mate.memory.config;
|
||||
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.boot.ApplicationArguments;
|
||||
import org.springframework.boot.ApplicationRunner;
|
||||
import org.springframework.core.annotation.Order;
|
||||
import org.springframework.jdbc.core.JdbcTemplate;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
/**
|
||||
* Memory 模块增量 Schema 迁移
|
||||
* <p>
|
||||
* 基础表 mate_memory_recall 已在 schema.sql / schema-mysql.sql 中定义。
|
||||
* 本类仅负责增量索引/字段迁移,确保从旧版本升级时自动补齐。
|
||||
*
|
||||
* @author MateClaw Team
|
||||
*/
|
||||
@Slf4j
|
||||
@Component
|
||||
@Order(201)
|
||||
@RequiredArgsConstructor
|
||||
public class MemorySchemaMigration implements ApplicationRunner {
|
||||
|
||||
private final JdbcTemplate jdbcTemplate;
|
||||
|
||||
@Override
|
||||
public void run(ApplicationArguments args) {
|
||||
// 增量索引补齐(v1.1 新增的复合索引,旧版本可能没有)
|
||||
safeExecute("CREATE INDEX IF NOT EXISTS idx_memory_recall_candidates ON mate_memory_recall(agent_id, promoted, deleted)");
|
||||
|
||||
// Session Search: MySQL FULLTEXT index on mate_message.content
|
||||
if (isMySql()) {
|
||||
safeExecute("ALTER TABLE mate_message ADD FULLTEXT INDEX ft_msg_content (content)");
|
||||
log.info("[MemorySchemaMigration] MySQL FULLTEXT index on mate_message.content created (or already exists)");
|
||||
}
|
||||
|
||||
// max_iterations 升级:旧版默认 10,新版 25。确保已有 agent 也更新
|
||||
safeExecute("UPDATE mate_agent SET max_iterations = 25 WHERE max_iterations = 10 AND deleted = 0");
|
||||
|
||||
log.debug("[MemorySchemaMigration] Incremental migration completed");
|
||||
}
|
||||
|
||||
private boolean isMySql() {
|
||||
try {
|
||||
String url = jdbcTemplate.getDataSource().getConnection().getMetaData().getURL();
|
||||
return url != null && url.contains("mysql");
|
||||
} catch (Exception e) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
private void safeExecute(String sql) {
|
||||
try {
|
||||
jdbcTemplate.execute(sql);
|
||||
} catch (Exception e) {
|
||||
log.debug("[MemorySchemaMigration] Migration skipped (may already exist): {}", e.getMessage());
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -56,7 +56,12 @@ public class EditFileTool {
|
||||
return errorResult(filePath, "oldText 和 newText 内容相同,无需替换");
|
||||
}
|
||||
|
||||
Path path = Paths.get(filePath).toAbsolutePath().normalize();
|
||||
Path path;
|
||||
try {
|
||||
path = vip.mate.tool.guard.WorkspacePathGuard.validatePath(filePath);
|
||||
} catch (IllegalArgumentException e) {
|
||||
return errorResult(filePath, e.getMessage());
|
||||
}
|
||||
|
||||
if (!Files.exists(path)) {
|
||||
return errorResult(filePath, "文件不存在: " + path);
|
||||
|
||||
@ -62,7 +62,12 @@ public class ReadFileTool {
|
||||
result.set("filePath", filePath);
|
||||
|
||||
try {
|
||||
Path path = Paths.get(filePath).toAbsolutePath().normalize();
|
||||
Path path;
|
||||
try {
|
||||
path = vip.mate.tool.guard.WorkspacePathGuard.validatePath(filePath);
|
||||
} catch (IllegalArgumentException e) {
|
||||
return errorResult(filePath, e.getMessage());
|
||||
}
|
||||
|
||||
// 文件存在性和类型校验
|
||||
if (!Files.exists(path)) {
|
||||
|
||||
@ -129,12 +129,22 @@ public class ShellExecuteTool {
|
||||
* Unix: /bin/sh -c command
|
||||
*/
|
||||
private static ProcessBuilder buildShellProcess(String command) {
|
||||
ProcessBuilder pb;
|
||||
if (IS_WINDOWS) {
|
||||
String winCommand = sanitizeWindowsCommand(command);
|
||||
return new ProcessBuilder("cmd.exe", "/D", "/S", "/C", winCommand);
|
||||
pb = new ProcessBuilder("cmd.exe", "/D", "/S", "/C", winCommand);
|
||||
} else {
|
||||
return new ProcessBuilder("/bin/sh", "-c", command);
|
||||
pb = new ProcessBuilder("/bin/sh", "-c", command);
|
||||
}
|
||||
|
||||
// 设置工作区活动目录
|
||||
java.nio.file.Path workingDir = vip.mate.tool.guard.WorkspacePathGuard.getWorkingDirectory();
|
||||
if (workingDir != null && java.nio.file.Files.isDirectory(workingDir)) {
|
||||
pb.directory(workingDir.toFile());
|
||||
log.info("[ShellExecute] Working directory set to: {}", workingDir);
|
||||
}
|
||||
|
||||
return pb;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@ -12,12 +12,21 @@ public final class ToolExecutionContext {
|
||||
|
||||
private static final ThreadLocal<String> CONVERSATION_ID = new ThreadLocal<>();
|
||||
private static final ThreadLocal<String> USERNAME = new ThreadLocal<>();
|
||||
/** 工作区活动目录(为空不限制) */
|
||||
private static final ThreadLocal<String> WORKSPACE_BASE_PATH = new ThreadLocal<>();
|
||||
|
||||
private ToolExecutionContext() {}
|
||||
|
||||
public static void set(String conversationId, String username) {
|
||||
CONVERSATION_ID.set(conversationId);
|
||||
USERNAME.set(username);
|
||||
WORKSPACE_BASE_PATH.remove();
|
||||
}
|
||||
|
||||
public static void set(String conversationId, String username, String workspaceBasePath) {
|
||||
CONVERSATION_ID.set(conversationId);
|
||||
USERNAME.set(username);
|
||||
WORKSPACE_BASE_PATH.set(workspaceBasePath);
|
||||
}
|
||||
|
||||
public static String conversationId() {
|
||||
@ -28,8 +37,14 @@ public final class ToolExecutionContext {
|
||||
return USERNAME.get();
|
||||
}
|
||||
|
||||
/** 获取当前工作区活动目录,为 null 表示不限制 */
|
||||
public static String workspaceBasePath() {
|
||||
return WORKSPACE_BASE_PATH.get();
|
||||
}
|
||||
|
||||
public static void clear() {
|
||||
CONVERSATION_ID.remove();
|
||||
USERNAME.remove();
|
||||
WORKSPACE_BASE_PATH.remove();
|
||||
}
|
||||
}
|
||||
|
||||
@ -48,7 +48,12 @@ public class WriteFileTool {
|
||||
content = "";
|
||||
}
|
||||
|
||||
Path path = Paths.get(filePath).toAbsolutePath().normalize();
|
||||
Path path;
|
||||
try {
|
||||
path = vip.mate.tool.guard.WorkspacePathGuard.validatePath(filePath);
|
||||
} catch (IllegalArgumentException e) {
|
||||
return errorResult(filePath, e.getMessage());
|
||||
}
|
||||
|
||||
// 如果路径是已有目录,拒绝
|
||||
if (Files.isDirectory(path)) {
|
||||
|
||||
@ -0,0 +1,81 @@
|
||||
package vip.mate.tool.guard;
|
||||
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import vip.mate.tool.builtin.ToolExecutionContext;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.Paths;
|
||||
|
||||
/**
|
||||
* 工作区路径沙箱校验器
|
||||
* <p>
|
||||
* 限制文件工具只能在工作区活动目录(basePath)及其子目录内操作。
|
||||
* 跨平台支持:Windows / macOS / Linux 路径统一处理。
|
||||
* <p>
|
||||
* 使用方式:在文件工具方法开头调用 {@link #validatePath(String)} 获取规范化路径。
|
||||
*
|
||||
* @author MateClaw Team
|
||||
*/
|
||||
@Slf4j
|
||||
public final class WorkspacePathGuard {
|
||||
|
||||
private WorkspacePathGuard() {}
|
||||
|
||||
/**
|
||||
* 校验文件路径是否在当前工作区活动目录范围内。
|
||||
* <p>
|
||||
* 从 {@link ToolExecutionContext#workspaceBasePath()} 读取当前活动目录。
|
||||
* 为空时不限制(向后兼容)。
|
||||
*
|
||||
* @param rawPath 用户传入的原始路径
|
||||
* @return 规范化后的绝对路径
|
||||
* @throws IllegalArgumentException 路径不在允许范围内
|
||||
*/
|
||||
public static Path validatePath(String rawPath) {
|
||||
Path normalized = Paths.get(rawPath).toAbsolutePath().normalize();
|
||||
|
||||
String basePath = ToolExecutionContext.workspaceBasePath();
|
||||
if (basePath == null || basePath.isBlank()) {
|
||||
return normalized; // 未配置活动目录,不限制
|
||||
}
|
||||
|
||||
Path root = Paths.get(basePath).toAbsolutePath().normalize();
|
||||
|
||||
// 先用 normalize 检查,再尝试 toRealPath 防符号链接逃逸
|
||||
if (!normalized.startsWith(root)) {
|
||||
throw new IllegalArgumentException(
|
||||
"路径不在工作区允许范围内: " + normalized + ",允许的根目录: " + root);
|
||||
}
|
||||
|
||||
// 对已存在的路径,解析符号链接后再次校验
|
||||
try {
|
||||
if (normalized.toFile().exists()) {
|
||||
Path realPath = normalized.toRealPath();
|
||||
Path realRoot = root.toFile().exists() ? root.toRealPath() : root;
|
||||
if (!realPath.startsWith(realRoot)) {
|
||||
throw new IllegalArgumentException(
|
||||
"路径通过符号链接逃逸出工作区: " + realPath + ",允许的根目录: " + realRoot);
|
||||
}
|
||||
return realPath;
|
||||
}
|
||||
} catch (IOException e) {
|
||||
log.debug("[WorkspacePathGuard] 无法解析真实路径(文件可能不存在): {}", normalized);
|
||||
}
|
||||
|
||||
return normalized;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取当前工作区活动目录的 Path(用于设置 Shell 工作目录等)。
|
||||
*
|
||||
* @return 活动目录 Path,未配置时返回 null
|
||||
*/
|
||||
public static Path getWorkingDirectory() {
|
||||
String basePath = ToolExecutionContext.workspaceBasePath();
|
||||
if (basePath == null || basePath.isBlank()) {
|
||||
return null;
|
||||
}
|
||||
return Paths.get(basePath).toAbsolutePath().normalize();
|
||||
}
|
||||
}
|
||||
@ -1,55 +0,0 @@
|
||||
package vip.mate.tool.guard.config;
|
||||
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.boot.ApplicationArguments;
|
||||
import org.springframework.boot.ApplicationRunner;
|
||||
import org.springframework.core.annotation.Order;
|
||||
import org.springframework.jdbc.core.JdbcTemplate;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
/**
|
||||
* ToolGuard 增量 Schema 迁移
|
||||
* <p>
|
||||
* 基础表 mate_tool_guard_rule / config / audit_log 已在 schema.sql / schema-mysql.sql 中定义。
|
||||
* 本类仅负责增量字段迁移,确保从旧版本升级时自动补齐新列。
|
||||
*
|
||||
* @author MateClaw Team
|
||||
*/
|
||||
@Slf4j
|
||||
@Component
|
||||
@Order(100)
|
||||
@RequiredArgsConstructor
|
||||
public class ToolGuardSchemaMigration implements ApplicationRunner {
|
||||
|
||||
private final JdbcTemplate jdbcTemplate;
|
||||
|
||||
@Override
|
||||
public void run(ApplicationArguments args) {
|
||||
migrateGuardConfigAuditColumns();
|
||||
}
|
||||
|
||||
/**
|
||||
* 为 mate_tool_guard_config 补充审计配置列(向已有表兼容迁移)
|
||||
*/
|
||||
private void migrateGuardConfigAuditColumns() {
|
||||
safeAddColumn("mate_tool_guard_config", "audit_enabled", "BOOLEAN NOT NULL DEFAULT TRUE");
|
||||
safeAddColumn("mate_tool_guard_config", "audit_min_severity", "VARCHAR(16) NOT NULL DEFAULT 'INFO'");
|
||||
safeAddColumn("mate_tool_guard_config", "audit_retention_days", "INT NOT NULL DEFAULT 90");
|
||||
log.debug("[ToolGuardSchemaMigration] Incremental migration completed");
|
||||
}
|
||||
|
||||
private void safeAddColumn(String table, String column, String definition) {
|
||||
try {
|
||||
Integer count = jdbcTemplate.queryForObject(
|
||||
"SELECT COUNT(*) FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_NAME = ? AND COLUMN_NAME = ?",
|
||||
Integer.class, table.toUpperCase(), column.toUpperCase());
|
||||
if (count != null && count > 0) {
|
||||
return;
|
||||
}
|
||||
jdbcTemplate.execute("ALTER TABLE " + table + " ADD COLUMN " + column + " " + definition);
|
||||
} catch (Exception e) {
|
||||
log.debug("[ToolGuardSchemaMigration] Column migration skipped: {}", e.getMessage());
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -30,7 +30,7 @@ import java.util.stream.Collectors;
|
||||
*/
|
||||
@Slf4j
|
||||
@Component
|
||||
@Order(110) // 在 ToolGuardSchemaMigration(100) 之后
|
||||
@Order(110) // Schema 由 Flyway 管理,在 Flyway 迁移完成后执行
|
||||
@RequiredArgsConstructor
|
||||
public class ToolGuardRuleSeedService implements ApplicationRunner {
|
||||
|
||||
|
||||
@ -1,52 +0,0 @@
|
||||
package vip.mate.wiki.config;
|
||||
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.boot.ApplicationArguments;
|
||||
import org.springframework.boot.ApplicationRunner;
|
||||
import org.springframework.core.annotation.Order;
|
||||
import org.springframework.jdbc.core.JdbcTemplate;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
/**
|
||||
* Wiki 模块增量 Schema 迁移
|
||||
* <p>
|
||||
* Wiki 基础表已在 schema.sql / schema-mysql.sql 中定义(随 DatabaseBootstrapRunner 初始化)。
|
||||
* 本类仅负责增量字段迁移,确保从旧版本升级时自动补齐新字段。
|
||||
* <p>
|
||||
* 如果表已由 schema.sql 创建(全新安装),这里的迁移是无副作用的空操作。
|
||||
*
|
||||
* @author MateClaw Team
|
||||
*/
|
||||
@Slf4j
|
||||
@Component
|
||||
@Order(202)
|
||||
@RequiredArgsConstructor
|
||||
public class WikiSchemaMigration implements ApplicationRunner {
|
||||
|
||||
private final JdbcTemplate jdbcTemplate;
|
||||
|
||||
@Override
|
||||
public void run(ApplicationArguments args) {
|
||||
migrateKnowledgeBaseColumns();
|
||||
}
|
||||
|
||||
/**
|
||||
* 增量字段迁移(兼容从 wiki 功能上线前的旧版本升级)
|
||||
*/
|
||||
private void migrateKnowledgeBaseColumns() {
|
||||
try {
|
||||
// v1.1: 添加 source_directory 列(本地目录扫描功能)
|
||||
jdbcTemplate.execute("ALTER TABLE mate_wiki_knowledge_base ADD COLUMN IF NOT EXISTS source_directory VARCHAR(512)");
|
||||
log.debug("[WikiSchemaMigration] Incremental migration completed");
|
||||
} catch (Exception e) {
|
||||
// MySQL 不支持 ADD COLUMN IF NOT EXISTS,忽略已存在的错误
|
||||
String msg = e.getMessage();
|
||||
if (msg != null && (msg.contains("Duplicate column") || msg.contains("already exists"))) {
|
||||
log.debug("[WikiSchemaMigration] Column source_directory already exists, skipping");
|
||||
} else {
|
||||
log.warn("[WikiSchemaMigration] Migration warning: {}", msg);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -7,6 +7,12 @@ import org.springframework.dao.DataAccessException;
|
||||
import org.springframework.jdbc.core.JdbcTemplate;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
/**
|
||||
* Conversation runtime data fixes (executed every startup).
|
||||
* <p>
|
||||
* Schema migrations have been moved to Flyway V4__conversation_extensions.sql.
|
||||
* This runner only handles data normalization that must run on every boot.
|
||||
*/
|
||||
@Slf4j
|
||||
@Component
|
||||
@RequiredArgsConstructor
|
||||
@ -16,13 +22,14 @@ public class ConversationSchemaMigration implements ApplicationRunner {
|
||||
|
||||
@Override
|
||||
public void run(org.springframework.boot.ApplicationArguments args) throws Exception {
|
||||
ensureColumn("mate_message", "content_parts", "TEXT");
|
||||
ensureColumn("mate_message", "prompt_tokens", "INT DEFAULT 0");
|
||||
ensureColumn("mate_message", "completion_tokens", "INT DEFAULT 0");
|
||||
ensureColumn("mate_message", "runtime_model", "VARCHAR(128)");
|
||||
ensureColumn("mate_message", "runtime_provider", "VARCHAR(64)");
|
||||
ensureColumn("mate_conversation", "stream_status", "VARCHAR(16) NOT NULL DEFAULT 'idle'");
|
||||
// 启动时重置孤儿状态:上次意外关机可能残留 running 状态
|
||||
resetOrphanedStreams();
|
||||
normalizeSharedChannelConversationOwners();
|
||||
}
|
||||
|
||||
/**
|
||||
* Reset orphaned 'running' conversations to 'idle' (crash recovery).
|
||||
*/
|
||||
private void resetOrphanedStreams() {
|
||||
try {
|
||||
int updated = jdbcTemplate.update(
|
||||
"UPDATE mate_conversation SET stream_status = 'idle' WHERE stream_status = 'running'");
|
||||
@ -32,8 +39,6 @@ public class ConversationSchemaMigration implements ApplicationRunner {
|
||||
} catch (DataAccessException e) {
|
||||
log.debug("Skipping orphan reset (table may not exist yet): {}", e.getMessage());
|
||||
}
|
||||
|
||||
normalizeSharedChannelConversationOwners();
|
||||
}
|
||||
|
||||
private void normalizeSharedChannelConversationOwners() {
|
||||
@ -61,21 +66,4 @@ public class ConversationSchemaMigration implements ApplicationRunner {
|
||||
log.debug("Skipping shared channel owner normalization: {}", e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
private void ensureColumn(String tableName, String columnName, String ddl) {
|
||||
String sql = "ALTER TABLE " + tableName + " ADD COLUMN IF NOT EXISTS " + columnName + " " + ddl;
|
||||
try {
|
||||
jdbcTemplate.execute(sql);
|
||||
log.info("Ensured schema column exists: {}.{}", tableName, columnName);
|
||||
} catch (DataAccessException e) {
|
||||
String message = e.getMessage() != null ? e.getMessage().toLowerCase() : "";
|
||||
if (message.contains("duplicate column") || message.contains("already exists")
|
||||
|| message.contains("not found")) {
|
||||
log.info("Schema migration skipped for {}.{}: {}", tableName, columnName,
|
||||
message.contains("not found") ? "table not yet created" : "column already exists");
|
||||
return;
|
||||
}
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -67,7 +67,7 @@ public class WorkspaceSchemaMigration implements ApplicationRunner {
|
||||
// 查找不在默认工作区中的用户
|
||||
int inserted = jdbcTemplate.update("""
|
||||
INSERT INTO mate_workspace_member (id, workspace_id, user_id, role, create_time, update_time, deleted)
|
||||
SELECT u.id, 1, u.id, u.role, NOW(), NOW(), 0
|
||||
SELECT u.id, 1, u.id, CASE WHEN u.role = 'admin' THEN 'owner' ELSE u.role END, NOW(), NOW(), 0
|
||||
FROM mate_user u
|
||||
WHERE u.deleted = 0
|
||||
AND NOT EXISTS (
|
||||
|
||||
@ -56,7 +56,7 @@ public class WorkspaceController {
|
||||
@PutMapping("/{id}")
|
||||
public R<WorkspaceEntity> update(@PathVariable Long id, @RequestBody WorkspaceEntity entity, Authentication auth) {
|
||||
Long userId = resolveUserId(auth);
|
||||
workspaceService.requirePermission(id, userId, "owner");
|
||||
workspaceService.requirePermission(id, userId, "admin");
|
||||
entity.setId(id);
|
||||
return R.ok(workspaceService.update(entity));
|
||||
}
|
||||
|
||||
@ -32,6 +32,9 @@ public class WorkspaceEntity {
|
||||
/** 拥有者用户 ID */
|
||||
private Long ownerId;
|
||||
|
||||
/** 工作区活动目录(限制文件工具的访问范围,为空不限制) */
|
||||
private String basePath;
|
||||
|
||||
/** 工作区级配置(JSON) */
|
||||
@TableField(value = "settings_json", updateStrategy = FieldStrategy.ALWAYS)
|
||||
private String settingsJson;
|
||||
|
||||
@ -97,12 +97,16 @@ public class WorkspaceService {
|
||||
|
||||
public WorkspaceEntity update(WorkspaceEntity entity) {
|
||||
WorkspaceEntity existing = getById(entity.getId());
|
||||
// slug 为 null 时保留原值,不做修改
|
||||
if (entity.getSlug() == null) {
|
||||
entity.setSlug(existing.getSlug());
|
||||
}
|
||||
// 不允许修改默认工作区的 slug
|
||||
if (DEFAULT_SLUG.equals(existing.getSlug()) && !DEFAULT_SLUG.equals(entity.getSlug())) {
|
||||
throw new MateClawException("不能修改默认工作区的标识");
|
||||
}
|
||||
// 验证 slug 唯一性(如果修改了 slug)
|
||||
if (entity.getSlug() != null && !entity.getSlug().equals(existing.getSlug())) {
|
||||
if (!entity.getSlug().equals(existing.getSlug())) {
|
||||
if (getBySlug(entity.getSlug()) != null) {
|
||||
throw new MateClawException("工作区标识已存在: " + entity.getSlug());
|
||||
}
|
||||
|
||||
@ -5,6 +5,10 @@ spring:
|
||||
username: ${DB_USERNAME:root}
|
||||
password: ${DB_PASSWORD:mateclaw123}
|
||||
|
||||
flyway:
|
||||
locations:
|
||||
- classpath:db/migration/mysql
|
||||
|
||||
h2:
|
||||
console:
|
||||
enabled: false
|
||||
|
||||
@ -25,6 +25,16 @@ spring:
|
||||
init:
|
||||
mode: never
|
||||
|
||||
# Flyway 数据库版本迁移
|
||||
flyway:
|
||||
enabled: true
|
||||
baseline-on-migrate: true
|
||||
baseline-version: "1"
|
||||
locations:
|
||||
- classpath:db/migration/h2
|
||||
validate-on-migrate: true
|
||||
clean-disabled: true
|
||||
|
||||
h2:
|
||||
console:
|
||||
enabled: true
|
||||
|
||||
@ -1236,6 +1236,23 @@ VALUES (1000000008, 'WeChat', 'weixin', 1000000001, '', '{
|
||||
'WeChat personal account channel (iLink Bot HTTP long polling). Get bot_token by scanning QR code to login, or enter existing token. Based on iLink Bot API, supports text, image, voice (ASR), file, and video messages', NOW(), NOW(), 0)
|
||||
ON DUPLICATE KEY UPDATE name=VALUES(name), channel_type=VALUES(channel_type), agent_id=VALUES(agent_id), bot_prefix=VALUES(bot_prefix), config_json=VALUES(config_json), enabled=VALUES(enabled), description=VALUES(description), update_time=VALUES(update_time), deleted=VALUES(deleted);
|
||||
|
||||
-- 9. Slack (disabled by default, requires bot_token / app_token)
|
||||
INSERT INTO mate_channel (id, name, channel_type, agent_id, bot_prefix, config_json, enabled, description, create_time, update_time, deleted)
|
||||
VALUES (1000000009, 'Slack Bot', 'slack', 1000000001, '', '{
|
||||
"bot_token": "",
|
||||
"app_token": "",
|
||||
"signing_secret": "",
|
||||
"dm_policy": "open",
|
||||
"group_policy": "mention",
|
||||
"allow_from": [],
|
||||
"deny_message": "Sorry, you do not have permission",
|
||||
"filter_thinking": true,
|
||||
"filter_tool_messages": true,
|
||||
"message_format": "auto"
|
||||
}', FALSE,
|
||||
'Slack channel (Socket Mode). Get Bot Token (xoxb-) and App-Level Token (xapp-) from Slack App settings, enable Socket Mode to start using.', NOW(), NOW(), 0)
|
||||
ON DUPLICATE KEY UPDATE name=VALUES(name), channel_type=VALUES(channel_type), agent_id=VALUES(agent_id), bot_prefix=VALUES(bot_prefix), config_json=VALUES(config_json), enabled=VALUES(enabled), description=VALUES(description), update_time=VALUES(update_time), deleted=VALUES(deleted);
|
||||
|
||||
-- ==================== Example Cron Jobs ====================
|
||||
INSERT INTO mate_cron_job (id, name, cron_expression, timezone, agent_id, task_type, trigger_message, request_body, enabled, create_time, update_time, deleted)
|
||||
VALUES (1000100001, 'Daily Greeting', '0 9 * * *', 'Asia/Shanghai', 1000000001, 'text', 'Good morning! Please give me today''s weather report and an inspirational quote.', NULL, TRUE, NOW(), NOW(), 0)
|
||||
|
||||
@ -1238,6 +1238,23 @@ VALUES (1000000008, '微信', 'weixin', 1000000001, '', '{
|
||||
'微信个人号渠道(iLink Bot HTTP 长轮询)。通过扫描二维码登录获取 bot_token,或直接填入已有 token。基于 iLink Bot API,支持文本、图片、语音(ASR)、文件、视频消息', NOW(), NOW(), 0)
|
||||
ON DUPLICATE KEY UPDATE name=VALUES(name), channel_type=VALUES(channel_type), agent_id=VALUES(agent_id), bot_prefix=VALUES(bot_prefix), config_json=VALUES(config_json), enabled=VALUES(enabled), description=VALUES(description), update_time=VALUES(update_time), deleted=VALUES(deleted);
|
||||
|
||||
-- 9. Slack(默认禁用,需配置 bot_token / app_token)
|
||||
INSERT INTO mate_channel (id, name, channel_type, agent_id, bot_prefix, config_json, enabled, description, create_time, update_time, deleted)
|
||||
VALUES (1000000009, 'Slack Bot', 'slack', 1000000001, '', '{
|
||||
"bot_token": "",
|
||||
"app_token": "",
|
||||
"signing_secret": "",
|
||||
"dm_policy": "open",
|
||||
"group_policy": "mention",
|
||||
"allow_from": [],
|
||||
"deny_message": "抱歉,您没有使用权限",
|
||||
"filter_thinking": true,
|
||||
"filter_tool_messages": true,
|
||||
"message_format": "auto"
|
||||
}', FALSE,
|
||||
'Slack 渠道(Socket Mode)。在 Slack App 后台获取 Bot Token(xoxb-)和 App-Level Token(xapp-),启用 Socket Mode 后即可使用。', NOW(), NOW(), 0)
|
||||
ON DUPLICATE KEY UPDATE name=VALUES(name), channel_type=VALUES(channel_type), agent_id=VALUES(agent_id), bot_prefix=VALUES(bot_prefix), config_json=VALUES(config_json), enabled=VALUES(enabled), description=VALUES(description), update_time=VALUES(update_time), deleted=VALUES(deleted);
|
||||
|
||||
-- ==================== 示例定时任务 ====================
|
||||
INSERT INTO mate_cron_job (id, name, cron_expression, timezone, agent_id, task_type, trigger_message, request_body, enabled, create_time, update_time, deleted)
|
||||
VALUES (1000100001, '每日问候', '0 9 * * *', 'Asia/Shanghai', 1000000001, 'text', '早上好!请给我今天的天气播报和一句励志名言。', NULL, TRUE, NOW(), NOW(), 0)
|
||||
|
||||
@ -0,0 +1,642 @@
|
||||
-- MateClaw 数据库初始化脚本
|
||||
-- 兼容 H2(开发模式)和 MySQL 8.0+(生产模式)
|
||||
|
||||
-- 用户表
|
||||
CREATE TABLE IF NOT EXISTS mate_user (
|
||||
id BIGINT NOT NULL PRIMARY KEY,
|
||||
username VARCHAR(64) NOT NULL UNIQUE,
|
||||
password VARCHAR(200) NOT NULL,
|
||||
nickname VARCHAR(64),
|
||||
avatar VARCHAR(256),
|
||||
email VARCHAR(128),
|
||||
role VARCHAR(32) NOT NULL DEFAULT 'user',
|
||||
enabled BOOLEAN NOT NULL DEFAULT TRUE,
|
||||
create_time DATETIME NOT NULL,
|
||||
update_time DATETIME NOT NULL,
|
||||
deleted INT NOT NULL DEFAULT 0
|
||||
);
|
||||
|
||||
-- Agent 配置表
|
||||
CREATE TABLE IF NOT EXISTS mate_agent (
|
||||
id BIGINT NOT NULL PRIMARY KEY,
|
||||
name VARCHAR(128) NOT NULL,
|
||||
description TEXT,
|
||||
agent_type VARCHAR(32) NOT NULL DEFAULT 'react',
|
||||
system_prompt TEXT,
|
||||
model_name VARCHAR(128),
|
||||
max_iterations INT NOT NULL DEFAULT 10,
|
||||
enabled BOOLEAN NOT NULL DEFAULT TRUE,
|
||||
icon VARCHAR(256),
|
||||
tags VARCHAR(256),
|
||||
workspace_id BIGINT NOT NULL DEFAULT 1,
|
||||
create_time DATETIME NOT NULL,
|
||||
update_time DATETIME NOT NULL,
|
||||
deleted INT NOT NULL DEFAULT 0
|
||||
);
|
||||
|
||||
-- 模型配置表
|
||||
CREATE TABLE IF NOT EXISTS mate_model_config (
|
||||
id BIGINT NOT NULL PRIMARY KEY,
|
||||
name VARCHAR(128) NOT NULL,
|
||||
provider VARCHAR(64) NOT NULL DEFAULT 'dashscope',
|
||||
model_name VARCHAR(128) NOT NULL,
|
||||
description TEXT,
|
||||
temperature DOUBLE,
|
||||
max_tokens INT,
|
||||
top_p DOUBLE,
|
||||
builtin BOOLEAN NOT NULL DEFAULT TRUE,
|
||||
enabled BOOLEAN NOT NULL DEFAULT TRUE,
|
||||
is_default BOOLEAN NOT NULL DEFAULT FALSE,
|
||||
max_input_tokens INT DEFAULT 0,
|
||||
enable_search BOOLEAN DEFAULT FALSE,
|
||||
search_strategy VARCHAR(32) DEFAULT NULL,
|
||||
create_time DATETIME NOT NULL,
|
||||
update_time DATETIME NOT NULL,
|
||||
deleted INT NOT NULL DEFAULT 0
|
||||
);
|
||||
|
||||
-- 模型 Provider 表
|
||||
CREATE TABLE IF NOT EXISTS mate_model_provider (
|
||||
provider_id VARCHAR(64) NOT NULL PRIMARY KEY,
|
||||
name VARCHAR(128) NOT NULL,
|
||||
api_key_prefix VARCHAR(32),
|
||||
chat_model VARCHAR(64),
|
||||
api_key VARCHAR(256),
|
||||
base_url VARCHAR(512),
|
||||
generate_kwargs TEXT,
|
||||
is_custom BOOLEAN NOT NULL DEFAULT FALSE,
|
||||
is_local BOOLEAN NOT NULL DEFAULT FALSE,
|
||||
support_model_discovery BOOLEAN NOT NULL DEFAULT FALSE,
|
||||
support_connection_check BOOLEAN NOT NULL DEFAULT FALSE,
|
||||
freeze_url BOOLEAN NOT NULL DEFAULT FALSE,
|
||||
require_api_key BOOLEAN NOT NULL DEFAULT TRUE,
|
||||
auth_type VARCHAR(16) NOT NULL DEFAULT 'api_key',
|
||||
oauth_access_token TEXT,
|
||||
oauth_refresh_token TEXT,
|
||||
oauth_expires_at BIGINT,
|
||||
oauth_account_id VARCHAR(128),
|
||||
create_time DATETIME NOT NULL,
|
||||
update_time DATETIME NOT NULL
|
||||
);
|
||||
|
||||
-- 系统设置表
|
||||
CREATE TABLE IF NOT EXISTS mate_system_setting (
|
||||
id BIGINT NOT NULL PRIMARY KEY,
|
||||
setting_key VARCHAR(128) NOT NULL UNIQUE,
|
||||
setting_value TEXT,
|
||||
description VARCHAR(256),
|
||||
create_time DATETIME NOT NULL,
|
||||
update_time DATETIME NOT NULL
|
||||
);
|
||||
|
||||
-- 技能表
|
||||
CREATE TABLE IF NOT EXISTS mate_skill (
|
||||
id BIGINT NOT NULL PRIMARY KEY,
|
||||
name VARCHAR(128) NOT NULL,
|
||||
description TEXT,
|
||||
skill_type VARCHAR(32) NOT NULL DEFAULT 'dynamic',
|
||||
icon VARCHAR(256),
|
||||
version VARCHAR(32),
|
||||
author VARCHAR(64),
|
||||
config_json TEXT,
|
||||
source_code TEXT,
|
||||
skill_content TEXT,
|
||||
enabled BOOLEAN NOT NULL DEFAULT TRUE,
|
||||
builtin BOOLEAN NOT NULL DEFAULT FALSE,
|
||||
tags VARCHAR(256),
|
||||
workspace_id BIGINT NOT NULL DEFAULT 1,
|
||||
create_time DATETIME NOT NULL,
|
||||
update_time DATETIME NOT NULL,
|
||||
deleted INT NOT NULL DEFAULT 0
|
||||
);
|
||||
|
||||
-- 工具表
|
||||
CREATE TABLE IF NOT EXISTS mate_tool (
|
||||
id BIGINT NOT NULL PRIMARY KEY,
|
||||
name VARCHAR(128) NOT NULL,
|
||||
display_name VARCHAR(128),
|
||||
description TEXT,
|
||||
tool_type VARCHAR(32) NOT NULL DEFAULT 'builtin',
|
||||
bean_name VARCHAR(128),
|
||||
icon VARCHAR(256),
|
||||
mcp_endpoint VARCHAR(256),
|
||||
params_schema TEXT,
|
||||
enabled BOOLEAN NOT NULL DEFAULT TRUE,
|
||||
builtin BOOLEAN NOT NULL DEFAULT FALSE,
|
||||
workspace_id BIGINT NOT NULL DEFAULT 1,
|
||||
create_time DATETIME NOT NULL,
|
||||
update_time DATETIME NOT NULL,
|
||||
deleted INT NOT NULL DEFAULT 0
|
||||
);
|
||||
|
||||
-- 渠道表
|
||||
CREATE TABLE IF NOT EXISTS mate_channel (
|
||||
id BIGINT NOT NULL PRIMARY KEY,
|
||||
name VARCHAR(128) NOT NULL,
|
||||
channel_type VARCHAR(32) NOT NULL,
|
||||
agent_id BIGINT,
|
||||
bot_prefix VARCHAR(64),
|
||||
config_json TEXT,
|
||||
enabled BOOLEAN NOT NULL DEFAULT FALSE,
|
||||
description VARCHAR(256),
|
||||
workspace_id BIGINT NOT NULL DEFAULT 1,
|
||||
create_time DATETIME NOT NULL,
|
||||
update_time DATETIME NOT NULL,
|
||||
deleted INT NOT NULL DEFAULT 0
|
||||
);
|
||||
|
||||
-- 会话表
|
||||
CREATE TABLE IF NOT EXISTS mate_conversation (
|
||||
id BIGINT NOT NULL PRIMARY KEY,
|
||||
conversation_id VARCHAR(64) NOT NULL UNIQUE,
|
||||
title VARCHAR(256),
|
||||
agent_id BIGINT,
|
||||
username VARCHAR(64),
|
||||
message_count INT NOT NULL DEFAULT 0,
|
||||
last_message TEXT,
|
||||
last_active_time DATETIME,
|
||||
stream_status VARCHAR(16) NOT NULL DEFAULT 'idle',
|
||||
workspace_id BIGINT NOT NULL DEFAULT 1,
|
||||
create_time DATETIME NOT NULL,
|
||||
update_time DATETIME NOT NULL,
|
||||
deleted INT NOT NULL DEFAULT 0
|
||||
);
|
||||
|
||||
-- 消息表
|
||||
CREATE TABLE IF NOT EXISTS mate_message (
|
||||
id BIGINT NOT NULL PRIMARY KEY,
|
||||
conversation_id VARCHAR(64) NOT NULL,
|
||||
role VARCHAR(32) NOT NULL,
|
||||
content TEXT,
|
||||
content_parts TEXT,
|
||||
tool_name VARCHAR(128),
|
||||
token_usage INT,
|
||||
prompt_tokens INT DEFAULT 0,
|
||||
completion_tokens INT DEFAULT 0,
|
||||
runtime_model VARCHAR(128),
|
||||
runtime_provider VARCHAR(64),
|
||||
status VARCHAR(32) NOT NULL DEFAULT 'completed',
|
||||
metadata JSON, -- 存储 toolCalls, plan, currentPhase, pendingApproval 等元数据
|
||||
create_time DATETIME NOT NULL,
|
||||
update_time DATETIME NOT NULL,
|
||||
deleted INT NOT NULL DEFAULT 0
|
||||
);
|
||||
|
||||
-- 执行计划表
|
||||
CREATE TABLE IF NOT EXISTS mate_plan (
|
||||
id BIGINT NOT NULL PRIMARY KEY,
|
||||
agent_id VARCHAR(64),
|
||||
goal TEXT,
|
||||
status VARCHAR(32) NOT NULL DEFAULT 'pending',
|
||||
total_steps INT NOT NULL DEFAULT 0,
|
||||
completed_steps INT NOT NULL DEFAULT 0,
|
||||
summary TEXT,
|
||||
start_time DATETIME,
|
||||
end_time DATETIME,
|
||||
create_time DATETIME NOT NULL,
|
||||
update_time DATETIME NOT NULL,
|
||||
deleted INT NOT NULL DEFAULT 0
|
||||
);
|
||||
|
||||
-- 子计划步骤表
|
||||
CREATE TABLE IF NOT EXISTS mate_sub_plan (
|
||||
id BIGINT NOT NULL PRIMARY KEY,
|
||||
plan_id BIGINT NOT NULL,
|
||||
step_index INT NOT NULL,
|
||||
description TEXT,
|
||||
status VARCHAR(32) NOT NULL DEFAULT 'pending',
|
||||
result TEXT,
|
||||
start_time DATETIME,
|
||||
end_time DATETIME,
|
||||
create_time DATETIME NOT NULL,
|
||||
update_time DATETIME NOT NULL,
|
||||
deleted INT NOT NULL DEFAULT 0
|
||||
);
|
||||
|
||||
-- 定时任务表
|
||||
CREATE TABLE IF NOT EXISTS mate_cron_job (
|
||||
id BIGINT NOT NULL PRIMARY KEY,
|
||||
name VARCHAR(128) NOT NULL,
|
||||
cron_expression VARCHAR(128) NOT NULL,
|
||||
timezone VARCHAR(64) NOT NULL DEFAULT 'Asia/Shanghai',
|
||||
agent_id BIGINT NOT NULL,
|
||||
task_type VARCHAR(16) NOT NULL DEFAULT 'text',
|
||||
trigger_message TEXT,
|
||||
request_body TEXT,
|
||||
enabled BOOLEAN NOT NULL DEFAULT TRUE,
|
||||
next_run_time DATETIME,
|
||||
last_run_time DATETIME,
|
||||
create_time DATETIME NOT NULL,
|
||||
update_time DATETIME NOT NULL,
|
||||
deleted INT NOT NULL DEFAULT 0
|
||||
);
|
||||
|
||||
-- 索引
|
||||
CREATE INDEX IF NOT EXISTS idx_message_conversation ON mate_message(conversation_id);
|
||||
CREATE INDEX IF NOT EXISTS idx_conversation_username ON mate_conversation(username);
|
||||
CREATE INDEX IF NOT EXISTS idx_sub_plan_plan_id ON mate_sub_plan(plan_id);
|
||||
CREATE INDEX IF NOT EXISTS idx_model_config_model_name ON mate_model_config(model_name);
|
||||
|
||||
-- 渠道会话存储表(主动推送标识缓存)
|
||||
CREATE TABLE IF NOT EXISTS mate_channel_session (
|
||||
id BIGINT NOT NULL PRIMARY KEY,
|
||||
conversation_id VARCHAR(128) NOT NULL UNIQUE,
|
||||
channel_type VARCHAR(32) NOT NULL,
|
||||
target_id VARCHAR(512) NOT NULL,
|
||||
sender_id VARCHAR(128),
|
||||
sender_name VARCHAR(128),
|
||||
channel_id BIGINT,
|
||||
last_active_time DATETIME NOT NULL,
|
||||
create_time DATETIME NOT NULL,
|
||||
update_time DATETIME NOT NULL,
|
||||
deleted INT NOT NULL DEFAULT 0
|
||||
);
|
||||
|
||||
CREATE INDEX IF NOT EXISTS idx_channel_session_type ON mate_channel_session(channel_type);
|
||||
CREATE INDEX IF NOT EXISTS idx_channel_session_channel_id ON mate_channel_session(channel_id);
|
||||
|
||||
-- 工作区文件表(Agent 级 Markdown 文档管理)
|
||||
CREATE TABLE IF NOT EXISTS mate_workspace_file (
|
||||
id BIGINT NOT NULL PRIMARY KEY,
|
||||
agent_id BIGINT NOT NULL,
|
||||
filename VARCHAR(256) NOT NULL,
|
||||
content CLOB,
|
||||
file_size BIGINT NOT NULL DEFAULT 0,
|
||||
enabled BOOLEAN NOT NULL DEFAULT FALSE,
|
||||
sort_order INT NOT NULL DEFAULT 0,
|
||||
create_time DATETIME NOT NULL,
|
||||
update_time DATETIME NOT NULL,
|
||||
deleted INT NOT NULL DEFAULT 0
|
||||
);
|
||||
|
||||
CREATE INDEX IF NOT EXISTS idx_workspace_file_agent ON mate_workspace_file(agent_id);
|
||||
|
||||
-- ==================== MCP Server 管理 ====================
|
||||
|
||||
-- MCP Server 配置表(独立于 mate_tool,一个 server 可暴露多个 tools)
|
||||
CREATE TABLE IF NOT EXISTS mate_mcp_server (
|
||||
id BIGINT NOT NULL PRIMARY KEY,
|
||||
name VARCHAR(128) NOT NULL,
|
||||
description TEXT,
|
||||
transport VARCHAR(32) NOT NULL DEFAULT 'stdio',
|
||||
url VARCHAR(512),
|
||||
headers_json TEXT,
|
||||
command VARCHAR(512),
|
||||
args_json TEXT,
|
||||
env_json TEXT,
|
||||
cwd VARCHAR(512),
|
||||
enabled BOOLEAN NOT NULL DEFAULT TRUE,
|
||||
connect_timeout_seconds INT NOT NULL DEFAULT 30,
|
||||
read_timeout_seconds INT NOT NULL DEFAULT 30,
|
||||
last_status VARCHAR(32) NOT NULL DEFAULT 'disconnected',
|
||||
last_error TEXT,
|
||||
last_connected_time DATETIME,
|
||||
tool_count INT NOT NULL DEFAULT 0,
|
||||
builtin BOOLEAN NOT NULL DEFAULT FALSE,
|
||||
create_time DATETIME NOT NULL,
|
||||
update_time DATETIME NOT NULL,
|
||||
deleted INT NOT NULL DEFAULT 0
|
||||
);
|
||||
|
||||
CREATE INDEX IF NOT EXISTS idx_mcp_server_enabled ON mate_mcp_server(enabled);
|
||||
|
||||
-- ==================== 工具安全治理(ToolGuard) ====================
|
||||
|
||||
-- 工具审批表
|
||||
CREATE TABLE IF NOT EXISTS mate_tool_approval (
|
||||
id BIGINT NOT NULL PRIMARY KEY,
|
||||
pending_id VARCHAR(32) NOT NULL UNIQUE,
|
||||
conversation_id VARCHAR(128) NOT NULL,
|
||||
user_id VARCHAR(64),
|
||||
agent_id VARCHAR(64),
|
||||
channel_type VARCHAR(32),
|
||||
requester_name VARCHAR(128),
|
||||
reply_target VARCHAR(512),
|
||||
tool_name VARCHAR(128) NOT NULL,
|
||||
tool_arguments TEXT,
|
||||
tool_call_payload TEXT,
|
||||
tool_call_hash VARCHAR(64),
|
||||
sibling_tool_calls TEXT,
|
||||
summary TEXT,
|
||||
findings_json TEXT,
|
||||
max_severity VARCHAR(16),
|
||||
status VARCHAR(32) NOT NULL DEFAULT 'PENDING',
|
||||
resolved_by VARCHAR(64),
|
||||
created_at DATETIME NOT NULL,
|
||||
resolved_at DATETIME,
|
||||
expire_at DATETIME,
|
||||
create_time DATETIME NOT NULL,
|
||||
update_time DATETIME NOT NULL,
|
||||
deleted INT NOT NULL DEFAULT 0
|
||||
);
|
||||
|
||||
CREATE INDEX IF NOT EXISTS idx_tool_approval_conv ON mate_tool_approval(conversation_id);
|
||||
CREATE INDEX IF NOT EXISTS idx_tool_approval_status ON mate_tool_approval(status);
|
||||
CREATE INDEX IF NOT EXISTS idx_tool_approval_pending_id ON mate_tool_approval(pending_id);
|
||||
|
||||
-- 安全规则表
|
||||
CREATE TABLE IF NOT EXISTS mate_tool_guard_rule (
|
||||
id BIGINT NOT NULL PRIMARY KEY,
|
||||
rule_id VARCHAR(64) NOT NULL UNIQUE,
|
||||
name VARCHAR(128) NOT NULL,
|
||||
description TEXT,
|
||||
tool_name VARCHAR(128),
|
||||
param_name VARCHAR(128),
|
||||
category VARCHAR(64) NOT NULL,
|
||||
severity VARCHAR(16) NOT NULL,
|
||||
decision VARCHAR(16) NOT NULL DEFAULT 'NEEDS_APPROVAL',
|
||||
pattern VARCHAR(512) NOT NULL,
|
||||
exclude_pattern VARCHAR(512),
|
||||
remediation TEXT,
|
||||
builtin BOOLEAN NOT NULL DEFAULT FALSE,
|
||||
enabled BOOLEAN NOT NULL DEFAULT TRUE,
|
||||
priority INT NOT NULL DEFAULT 100,
|
||||
create_time DATETIME NOT NULL,
|
||||
update_time DATETIME NOT NULL,
|
||||
deleted INT NOT NULL DEFAULT 0
|
||||
);
|
||||
|
||||
-- 安全全局配置表
|
||||
CREATE TABLE IF NOT EXISTS mate_tool_guard_config (
|
||||
id BIGINT NOT NULL PRIMARY KEY,
|
||||
enabled BOOLEAN NOT NULL DEFAULT TRUE,
|
||||
guard_scope VARCHAR(32) NOT NULL DEFAULT 'all',
|
||||
guarded_tools_json TEXT,
|
||||
denied_tools_json TEXT,
|
||||
file_guard_enabled BOOLEAN NOT NULL DEFAULT TRUE,
|
||||
sensitive_paths_json TEXT,
|
||||
audit_enabled BOOLEAN NOT NULL DEFAULT TRUE,
|
||||
audit_min_severity VARCHAR(16) NOT NULL DEFAULT 'INFO',
|
||||
audit_retention_days INT NOT NULL DEFAULT 90,
|
||||
create_time DATETIME NOT NULL,
|
||||
update_time DATETIME NOT NULL
|
||||
);
|
||||
|
||||
-- 安全审计日志表
|
||||
CREATE TABLE IF NOT EXISTS mate_tool_guard_audit_log (
|
||||
id BIGINT NOT NULL PRIMARY KEY,
|
||||
conversation_id VARCHAR(128),
|
||||
agent_id VARCHAR(64),
|
||||
user_id VARCHAR(64),
|
||||
channel_type VARCHAR(32),
|
||||
tool_name VARCHAR(128) NOT NULL,
|
||||
tool_params_json TEXT,
|
||||
decision VARCHAR(16) NOT NULL,
|
||||
max_severity VARCHAR(16),
|
||||
findings_json TEXT,
|
||||
pending_id VARCHAR(32),
|
||||
replay_payload_hash VARCHAR(64),
|
||||
create_time DATETIME NOT NULL,
|
||||
update_time DATETIME NOT NULL,
|
||||
deleted INT NOT NULL DEFAULT 0
|
||||
);
|
||||
|
||||
-- 外部数据源表(查数功能)
|
||||
CREATE TABLE IF NOT EXISTS mate_datasource (
|
||||
id BIGINT NOT NULL PRIMARY KEY,
|
||||
name VARCHAR(128) NOT NULL,
|
||||
description VARCHAR(512),
|
||||
db_type VARCHAR(32) NOT NULL,
|
||||
host VARCHAR(256) NOT NULL,
|
||||
port INT NOT NULL,
|
||||
database_name VARCHAR(128) NOT NULL,
|
||||
username VARCHAR(128),
|
||||
password VARCHAR(512),
|
||||
extra_params VARCHAR(512),
|
||||
schema_name VARCHAR(128),
|
||||
enabled BOOLEAN NOT NULL DEFAULT TRUE,
|
||||
last_test_time DATETIME,
|
||||
last_test_ok BOOLEAN,
|
||||
create_time DATETIME NOT NULL,
|
||||
update_time DATETIME NOT NULL,
|
||||
deleted INT NOT NULL DEFAULT 0
|
||||
);
|
||||
|
||||
CREATE INDEX IF NOT EXISTS idx_guard_audit_conv ON mate_tool_guard_audit_log(conversation_id);
|
||||
CREATE INDEX IF NOT EXISTS idx_guard_audit_time ON mate_tool_guard_audit_log(create_time);
|
||||
|
||||
-- 记忆召回追踪表(Dreaming 评分驱动记忆整合)
|
||||
CREATE TABLE IF NOT EXISTS mate_memory_recall (
|
||||
id BIGINT NOT NULL PRIMARY KEY,
|
||||
agent_id BIGINT NOT NULL,
|
||||
filename VARCHAR(256) NOT NULL,
|
||||
snippet_hash VARCHAR(64),
|
||||
snippet_preview VARCHAR(512),
|
||||
recall_count INT NOT NULL DEFAULT 0,
|
||||
daily_count INT NOT NULL DEFAULT 0,
|
||||
query_hashes TEXT,
|
||||
score DOUBLE NOT NULL DEFAULT 0.0,
|
||||
last_recalled_at DATETIME,
|
||||
promoted BOOLEAN NOT NULL DEFAULT FALSE,
|
||||
create_time DATETIME NOT NULL,
|
||||
update_time DATETIME NOT NULL,
|
||||
deleted INT NOT NULL DEFAULT 0
|
||||
);
|
||||
|
||||
CREATE INDEX IF NOT EXISTS idx_memory_recall_agent ON mate_memory_recall(agent_id);
|
||||
CREATE INDEX IF NOT EXISTS idx_memory_recall_agent_file ON mate_memory_recall(agent_id, filename);
|
||||
CREATE INDEX IF NOT EXISTS idx_memory_recall_score ON mate_memory_recall(agent_id, score);
|
||||
CREATE INDEX IF NOT EXISTS idx_memory_recall_candidates ON mate_memory_recall(agent_id, promoted, deleted);
|
||||
|
||||
-- 补充复合索引(高频查询优化)
|
||||
CREATE INDEX IF NOT EXISTS idx_message_conv_time ON mate_message(conversation_id, create_time);
|
||||
CREATE INDEX IF NOT EXISTS idx_workspace_file_agent_enabled ON mate_workspace_file(agent_id, enabled);
|
||||
|
||||
-- 清理 Codex 不支持的 ChatGPT OAuth 模型(gpt-4o, o3, o4-mini 在 Codex 模式下不可用)
|
||||
DELETE FROM mate_model_config WHERE provider = 'openai-chatgpt' AND model_name IN ('gpt-4o', 'o3', 'o4-mini');
|
||||
|
||||
-- ==================== 异步任务(视频/图片生成等长耗时操作) ====================
|
||||
|
||||
CREATE TABLE IF NOT EXISTS mate_async_task (
|
||||
id BIGINT NOT NULL PRIMARY KEY,
|
||||
task_id VARCHAR(64) NOT NULL UNIQUE,
|
||||
task_type VARCHAR(32) NOT NULL,
|
||||
status VARCHAR(16) NOT NULL DEFAULT 'pending',
|
||||
conversation_id VARCHAR(128),
|
||||
message_id BIGINT,
|
||||
provider_name VARCHAR(64),
|
||||
provider_task_id VARCHAR(128),
|
||||
request_json TEXT,
|
||||
result_json TEXT,
|
||||
error_message VARCHAR(512),
|
||||
progress INT DEFAULT 0,
|
||||
created_by VARCHAR(64),
|
||||
create_time DATETIME NOT NULL,
|
||||
update_time DATETIME NOT NULL
|
||||
);
|
||||
|
||||
CREATE INDEX IF NOT EXISTS idx_async_task_conv ON mate_async_task(conversation_id);
|
||||
CREATE INDEX IF NOT EXISTS idx_async_task_status ON mate_async_task(status);
|
||||
|
||||
-- ==================== Wiki 知识库 ====================
|
||||
|
||||
CREATE TABLE IF NOT EXISTS mate_wiki_knowledge_base (
|
||||
id BIGINT NOT NULL PRIMARY KEY,
|
||||
name VARCHAR(128) NOT NULL,
|
||||
description TEXT,
|
||||
agent_id BIGINT,
|
||||
config_content CLOB,
|
||||
source_directory VARCHAR(512),
|
||||
status VARCHAR(32) NOT NULL DEFAULT 'active',
|
||||
page_count INT NOT NULL DEFAULT 0,
|
||||
raw_count INT NOT NULL DEFAULT 0,
|
||||
workspace_id BIGINT NOT NULL DEFAULT 1,
|
||||
create_time DATETIME NOT NULL,
|
||||
update_time DATETIME NOT NULL,
|
||||
deleted INT NOT NULL DEFAULT 0
|
||||
);
|
||||
CREATE INDEX IF NOT EXISTS idx_wiki_kb_agent ON mate_wiki_knowledge_base(agent_id);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS mate_wiki_raw_material (
|
||||
id BIGINT NOT NULL PRIMARY KEY,
|
||||
kb_id BIGINT NOT NULL,
|
||||
title VARCHAR(256) NOT NULL,
|
||||
source_type VARCHAR(32) NOT NULL DEFAULT 'text',
|
||||
source_path VARCHAR(512),
|
||||
original_content CLOB,
|
||||
extracted_text CLOB,
|
||||
content_hash VARCHAR(64),
|
||||
file_size BIGINT NOT NULL DEFAULT 0,
|
||||
processing_status VARCHAR(32) NOT NULL DEFAULT 'pending',
|
||||
last_processed_at DATETIME,
|
||||
error_message VARCHAR(512),
|
||||
create_time DATETIME NOT NULL,
|
||||
update_time DATETIME NOT NULL,
|
||||
deleted INT NOT NULL DEFAULT 0
|
||||
);
|
||||
CREATE INDEX IF NOT EXISTS idx_wiki_raw_kb ON mate_wiki_raw_material(kb_id);
|
||||
CREATE INDEX IF NOT EXISTS idx_wiki_raw_status ON mate_wiki_raw_material(kb_id, processing_status);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS mate_wiki_page (
|
||||
id BIGINT NOT NULL PRIMARY KEY,
|
||||
kb_id BIGINT NOT NULL,
|
||||
slug VARCHAR(256) NOT NULL,
|
||||
title VARCHAR(256) NOT NULL,
|
||||
content CLOB,
|
||||
summary VARCHAR(1024),
|
||||
outgoing_links CLOB,
|
||||
source_raw_ids CLOB,
|
||||
version INT NOT NULL DEFAULT 1,
|
||||
last_updated_by VARCHAR(32) NOT NULL DEFAULT 'ai',
|
||||
create_time DATETIME NOT NULL,
|
||||
update_time DATETIME NOT NULL,
|
||||
deleted INT NOT NULL DEFAULT 0,
|
||||
CONSTRAINT uk_wiki_page_kb_slug UNIQUE (kb_id, slug)
|
||||
);
|
||||
CREATE INDEX IF NOT EXISTS idx_wiki_page_kb ON mate_wiki_page(kb_id);
|
||||
|
||||
-- =============================================
|
||||
-- 工作区表(Phase 2)
|
||||
-- =============================================
|
||||
|
||||
-- 工作区
|
||||
CREATE TABLE IF NOT EXISTS mate_workspace (
|
||||
id BIGINT NOT NULL PRIMARY KEY,
|
||||
name VARCHAR(128) NOT NULL,
|
||||
slug VARCHAR(64) NOT NULL,
|
||||
description VARCHAR(256),
|
||||
owner_id BIGINT,
|
||||
settings_json TEXT,
|
||||
base_path VARCHAR(512),
|
||||
create_time DATETIME NOT NULL,
|
||||
update_time DATETIME NOT NULL,
|
||||
deleted INT NOT NULL DEFAULT 0,
|
||||
CONSTRAINT uk_workspace_slug UNIQUE (slug)
|
||||
);
|
||||
|
||||
-- 工作区成员
|
||||
CREATE TABLE IF NOT EXISTS mate_workspace_member (
|
||||
id BIGINT NOT NULL PRIMARY KEY,
|
||||
workspace_id BIGINT NOT NULL,
|
||||
user_id BIGINT NOT NULL,
|
||||
role VARCHAR(32) NOT NULL DEFAULT 'member',
|
||||
create_time DATETIME NOT NULL,
|
||||
update_time DATETIME NOT NULL,
|
||||
deleted INT NOT NULL DEFAULT 0
|
||||
);
|
||||
CREATE INDEX IF NOT EXISTS idx_ws_member_workspace ON mate_workspace_member(workspace_id);
|
||||
CREATE INDEX IF NOT EXISTS idx_ws_member_user ON mate_workspace_member(user_id);
|
||||
|
||||
-- =============================================
|
||||
-- Agent-Skill / Agent-Tool 绑定表(Phase 3 Sprint 2)
|
||||
-- =============================================
|
||||
|
||||
CREATE TABLE IF NOT EXISTS mate_agent_skill (
|
||||
id BIGINT NOT NULL PRIMARY KEY,
|
||||
agent_id BIGINT NOT NULL,
|
||||
skill_id BIGINT NOT NULL,
|
||||
enabled BOOLEAN NOT NULL DEFAULT TRUE,
|
||||
config_json TEXT,
|
||||
create_time DATETIME NOT NULL,
|
||||
update_time DATETIME NOT NULL,
|
||||
deleted INT NOT NULL DEFAULT 0
|
||||
);
|
||||
CREATE UNIQUE INDEX IF NOT EXISTS uk_agent_skill ON mate_agent_skill(agent_id, skill_id);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS mate_agent_tool (
|
||||
id BIGINT NOT NULL PRIMARY KEY,
|
||||
agent_id BIGINT NOT NULL,
|
||||
tool_name VARCHAR(128) NOT NULL,
|
||||
enabled BOOLEAN NOT NULL DEFAULT TRUE,
|
||||
create_time DATETIME NOT NULL,
|
||||
update_time DATETIME NOT NULL,
|
||||
deleted INT NOT NULL DEFAULT 0
|
||||
);
|
||||
CREATE UNIQUE INDEX IF NOT EXISTS uk_agent_tool ON mate_agent_tool(agent_id, tool_name);
|
||||
|
||||
-- =============================================
|
||||
-- CronJob 执行历史(Phase 3 Sprint 3)
|
||||
-- =============================================
|
||||
CREATE TABLE IF NOT EXISTS mate_cron_job_run (
|
||||
id BIGINT NOT NULL PRIMARY KEY,
|
||||
cron_job_id BIGINT NOT NULL,
|
||||
conversation_id VARCHAR(64),
|
||||
status VARCHAR(32) NOT NULL,
|
||||
trigger_type VARCHAR(32) NOT NULL DEFAULT 'scheduled',
|
||||
started_at DATETIME NOT NULL,
|
||||
finished_at DATETIME,
|
||||
error_message TEXT,
|
||||
token_usage INT DEFAULT 0,
|
||||
create_time DATETIME NOT NULL
|
||||
);
|
||||
CREATE INDEX IF NOT EXISTS idx_cron_run_job ON mate_cron_job_run(cron_job_id, started_at);
|
||||
|
||||
-- =============================================
|
||||
-- 用量日统计(Phase 3 Sprint 3)
|
||||
-- =============================================
|
||||
CREATE TABLE IF NOT EXISTS mate_usage_daily (
|
||||
id BIGINT NOT NULL PRIMARY KEY,
|
||||
workspace_id BIGINT NOT NULL,
|
||||
agent_id BIGINT,
|
||||
stat_date DATE NOT NULL,
|
||||
conversation_count INT DEFAULT 0,
|
||||
message_count INT DEFAULT 0,
|
||||
total_tokens BIGINT DEFAULT 0,
|
||||
prompt_tokens BIGINT DEFAULT 0,
|
||||
completion_tokens BIGINT DEFAULT 0,
|
||||
tool_call_count INT DEFAULT 0,
|
||||
error_count INT DEFAULT 0,
|
||||
create_time DATETIME NOT NULL
|
||||
);
|
||||
CREATE UNIQUE INDEX IF NOT EXISTS uk_usage_daily ON mate_usage_daily(workspace_id, agent_id, stat_date);
|
||||
|
||||
-- =============================================
|
||||
-- 操作审计事件表(Phase 3 Sprint 1)
|
||||
-- =============================================
|
||||
CREATE TABLE IF NOT EXISTS mate_audit_event (
|
||||
id BIGINT NOT NULL PRIMARY KEY,
|
||||
workspace_id BIGINT,
|
||||
user_id BIGINT NOT NULL,
|
||||
username VARCHAR(64) NOT NULL,
|
||||
action VARCHAR(64) NOT NULL,
|
||||
resource_type VARCHAR(64) NOT NULL,
|
||||
resource_id VARCHAR(128),
|
||||
resource_name VARCHAR(256),
|
||||
detail_json TEXT,
|
||||
ip_address VARCHAR(64),
|
||||
user_agent VARCHAR(256),
|
||||
create_time DATETIME NOT NULL
|
||||
);
|
||||
CREATE INDEX IF NOT EXISTS idx_audit_ws_time ON mate_audit_event(workspace_id, create_time);
|
||||
CREATE INDEX IF NOT EXISTS idx_audit_user ON mate_audit_event(user_id);
|
||||
CREATE INDEX IF NOT EXISTS idx_audit_resource ON mate_audit_event(resource_type, resource_id);
|
||||
@ -0,0 +1,629 @@
|
||||
-- MateClaw 数据库初始化脚本(MySQL / MariaDB 专用)
|
||||
|
||||
-- 用户表
|
||||
CREATE TABLE IF NOT EXISTS mate_user (
|
||||
id BIGINT NOT NULL PRIMARY KEY,
|
||||
username VARCHAR(64) NOT NULL UNIQUE,
|
||||
password VARCHAR(200) NOT NULL,
|
||||
nickname VARCHAR(64),
|
||||
avatar VARCHAR(256),
|
||||
email VARCHAR(128),
|
||||
role VARCHAR(32) NOT NULL DEFAULT 'user',
|
||||
enabled TINYINT(1) NOT NULL DEFAULT 1,
|
||||
create_time DATETIME NOT NULL,
|
||||
update_time DATETIME NOT NULL,
|
||||
deleted INT NOT NULL DEFAULT 0
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
|
||||
|
||||
-- Agent 配置表
|
||||
CREATE TABLE IF NOT EXISTS mate_agent (
|
||||
id BIGINT NOT NULL PRIMARY KEY,
|
||||
name VARCHAR(128) NOT NULL,
|
||||
description TEXT,
|
||||
agent_type VARCHAR(32) NOT NULL DEFAULT 'react',
|
||||
system_prompt TEXT,
|
||||
model_name VARCHAR(128),
|
||||
max_iterations INT NOT NULL DEFAULT 10,
|
||||
enabled TINYINT(1) NOT NULL DEFAULT 1,
|
||||
icon VARCHAR(256),
|
||||
tags VARCHAR(256),
|
||||
workspace_id BIGINT NOT NULL DEFAULT 1,
|
||||
create_time DATETIME NOT NULL,
|
||||
update_time DATETIME NOT NULL,
|
||||
deleted INT NOT NULL DEFAULT 0
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
|
||||
|
||||
-- 模型配置表
|
||||
CREATE TABLE IF NOT EXISTS mate_model_config (
|
||||
id BIGINT NOT NULL PRIMARY KEY,
|
||||
name VARCHAR(128) NOT NULL,
|
||||
provider VARCHAR(64) NOT NULL DEFAULT 'dashscope',
|
||||
model_name VARCHAR(128) NOT NULL,
|
||||
description TEXT,
|
||||
temperature DOUBLE,
|
||||
max_tokens INT,
|
||||
top_p DOUBLE,
|
||||
builtin TINYINT(1) NOT NULL DEFAULT 1,
|
||||
enabled TINYINT(1) NOT NULL DEFAULT 1,
|
||||
is_default TINYINT(1) NOT NULL DEFAULT 0,
|
||||
max_input_tokens INT DEFAULT 0,
|
||||
enable_search TINYINT(1) DEFAULT 0,
|
||||
search_strategy VARCHAR(32) DEFAULT NULL,
|
||||
create_time DATETIME NOT NULL,
|
||||
update_time DATETIME NOT NULL,
|
||||
deleted INT NOT NULL DEFAULT 0,
|
||||
INDEX idx_model_config_model_name (model_name)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
|
||||
|
||||
-- 模型 Provider 表
|
||||
CREATE TABLE IF NOT EXISTS mate_model_provider (
|
||||
provider_id VARCHAR(64) NOT NULL PRIMARY KEY,
|
||||
name VARCHAR(128) NOT NULL,
|
||||
api_key_prefix VARCHAR(32),
|
||||
chat_model VARCHAR(64),
|
||||
api_key VARCHAR(256),
|
||||
base_url VARCHAR(512),
|
||||
generate_kwargs TEXT,
|
||||
is_custom TINYINT(1) NOT NULL DEFAULT 0,
|
||||
is_local TINYINT(1) NOT NULL DEFAULT 0,
|
||||
support_model_discovery TINYINT(1) NOT NULL DEFAULT 0,
|
||||
support_connection_check TINYINT(1) NOT NULL DEFAULT 0,
|
||||
freeze_url TINYINT(1) NOT NULL DEFAULT 0,
|
||||
require_api_key TINYINT(1) NOT NULL DEFAULT 1,
|
||||
auth_type VARCHAR(16) NOT NULL DEFAULT 'api_key',
|
||||
oauth_access_token TEXT,
|
||||
oauth_refresh_token TEXT,
|
||||
oauth_expires_at BIGINT,
|
||||
oauth_account_id VARCHAR(128),
|
||||
create_time DATETIME NOT NULL,
|
||||
update_time DATETIME NOT NULL
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
|
||||
|
||||
-- 系统设置表
|
||||
CREATE TABLE IF NOT EXISTS mate_system_setting (
|
||||
id BIGINT NOT NULL PRIMARY KEY,
|
||||
setting_key VARCHAR(128) NOT NULL UNIQUE,
|
||||
setting_value TEXT,
|
||||
description VARCHAR(256),
|
||||
create_time DATETIME NOT NULL,
|
||||
update_time DATETIME NOT NULL
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
|
||||
|
||||
-- 技能表
|
||||
CREATE TABLE IF NOT EXISTS mate_skill (
|
||||
id BIGINT NOT NULL PRIMARY KEY,
|
||||
name VARCHAR(128) NOT NULL,
|
||||
description TEXT,
|
||||
skill_type VARCHAR(32) NOT NULL DEFAULT 'dynamic',
|
||||
icon VARCHAR(256),
|
||||
version VARCHAR(32),
|
||||
author VARCHAR(64),
|
||||
config_json TEXT,
|
||||
source_code TEXT,
|
||||
skill_content TEXT,
|
||||
enabled TINYINT(1) NOT NULL DEFAULT 1,
|
||||
builtin TINYINT(1) NOT NULL DEFAULT 0,
|
||||
tags VARCHAR(256),
|
||||
workspace_id BIGINT NOT NULL DEFAULT 1,
|
||||
create_time DATETIME NOT NULL,
|
||||
update_time DATETIME NOT NULL,
|
||||
deleted INT NOT NULL DEFAULT 0
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
|
||||
|
||||
-- 工具表
|
||||
CREATE TABLE IF NOT EXISTS mate_tool (
|
||||
id BIGINT NOT NULL PRIMARY KEY,
|
||||
name VARCHAR(128) NOT NULL,
|
||||
display_name VARCHAR(128),
|
||||
description TEXT,
|
||||
tool_type VARCHAR(32) NOT NULL DEFAULT 'builtin',
|
||||
bean_name VARCHAR(128),
|
||||
icon VARCHAR(256),
|
||||
mcp_endpoint VARCHAR(256),
|
||||
params_schema TEXT,
|
||||
enabled TINYINT(1) NOT NULL DEFAULT 1,
|
||||
builtin TINYINT(1) NOT NULL DEFAULT 0,
|
||||
workspace_id BIGINT NOT NULL DEFAULT 1,
|
||||
create_time DATETIME NOT NULL,
|
||||
update_time DATETIME NOT NULL,
|
||||
deleted INT NOT NULL DEFAULT 0
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
|
||||
|
||||
-- 渠道表
|
||||
CREATE TABLE IF NOT EXISTS mate_channel (
|
||||
id BIGINT NOT NULL PRIMARY KEY,
|
||||
name VARCHAR(128) NOT NULL,
|
||||
channel_type VARCHAR(32) NOT NULL,
|
||||
agent_id BIGINT,
|
||||
bot_prefix VARCHAR(64),
|
||||
config_json TEXT,
|
||||
enabled TINYINT(1) NOT NULL DEFAULT 0,
|
||||
description VARCHAR(256),
|
||||
workspace_id BIGINT NOT NULL DEFAULT 1,
|
||||
create_time DATETIME NOT NULL,
|
||||
update_time DATETIME NOT NULL,
|
||||
deleted INT NOT NULL DEFAULT 0
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
|
||||
|
||||
-- 会话表
|
||||
CREATE TABLE IF NOT EXISTS mate_conversation (
|
||||
id BIGINT NOT NULL PRIMARY KEY,
|
||||
conversation_id VARCHAR(64) NOT NULL UNIQUE,
|
||||
title VARCHAR(256),
|
||||
agent_id BIGINT,
|
||||
username VARCHAR(64),
|
||||
message_count INT NOT NULL DEFAULT 0,
|
||||
last_message TEXT,
|
||||
last_active_time DATETIME,
|
||||
stream_status VARCHAR(16) NOT NULL DEFAULT 'idle',
|
||||
workspace_id BIGINT NOT NULL DEFAULT 1,
|
||||
create_time DATETIME NOT NULL,
|
||||
update_time DATETIME NOT NULL,
|
||||
deleted INT NOT NULL DEFAULT 0,
|
||||
INDEX idx_conversation_username (username)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
|
||||
|
||||
-- 消息表
|
||||
CREATE TABLE IF NOT EXISTS mate_message (
|
||||
id BIGINT NOT NULL PRIMARY KEY,
|
||||
conversation_id VARCHAR(64) NOT NULL,
|
||||
role VARCHAR(32) NOT NULL,
|
||||
content TEXT,
|
||||
content_parts TEXT,
|
||||
tool_name VARCHAR(128),
|
||||
token_usage INT,
|
||||
prompt_tokens INT DEFAULT 0,
|
||||
completion_tokens INT DEFAULT 0,
|
||||
runtime_model VARCHAR(128),
|
||||
runtime_provider VARCHAR(64),
|
||||
status VARCHAR(32) NOT NULL DEFAULT 'completed',
|
||||
metadata JSON COMMENT '存储 toolCalls, plan, currentPhase, pendingApproval 等元数据',
|
||||
create_time DATETIME NOT NULL,
|
||||
update_time DATETIME NOT NULL,
|
||||
deleted INT NOT NULL DEFAULT 0,
|
||||
INDEX idx_message_conversation (conversation_id),
|
||||
INDEX idx_message_conv_time (conversation_id, create_time)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
|
||||
|
||||
-- 执行计划表
|
||||
CREATE TABLE IF NOT EXISTS mate_plan (
|
||||
id BIGINT NOT NULL PRIMARY KEY,
|
||||
agent_id VARCHAR(64),
|
||||
goal TEXT,
|
||||
status VARCHAR(32) NOT NULL DEFAULT 'pending',
|
||||
total_steps INT NOT NULL DEFAULT 0,
|
||||
completed_steps INT NOT NULL DEFAULT 0,
|
||||
summary TEXT,
|
||||
start_time DATETIME,
|
||||
end_time DATETIME,
|
||||
create_time DATETIME NOT NULL,
|
||||
update_time DATETIME NOT NULL,
|
||||
deleted INT NOT NULL DEFAULT 0
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
|
||||
|
||||
-- 子计划步骤表
|
||||
CREATE TABLE IF NOT EXISTS mate_sub_plan (
|
||||
id BIGINT NOT NULL PRIMARY KEY,
|
||||
plan_id BIGINT NOT NULL,
|
||||
step_index INT NOT NULL,
|
||||
description TEXT,
|
||||
status VARCHAR(32) NOT NULL DEFAULT 'pending',
|
||||
result TEXT,
|
||||
start_time DATETIME,
|
||||
end_time DATETIME,
|
||||
create_time DATETIME NOT NULL,
|
||||
update_time DATETIME NOT NULL,
|
||||
deleted INT NOT NULL DEFAULT 0,
|
||||
INDEX idx_sub_plan_plan_id (plan_id)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
|
||||
|
||||
-- 定时任务表
|
||||
CREATE TABLE IF NOT EXISTS mate_cron_job (
|
||||
id BIGINT NOT NULL PRIMARY KEY,
|
||||
name VARCHAR(128) NOT NULL,
|
||||
cron_expression VARCHAR(128) NOT NULL,
|
||||
timezone VARCHAR(64) NOT NULL DEFAULT 'Asia/Shanghai',
|
||||
agent_id BIGINT NOT NULL,
|
||||
task_type VARCHAR(16) NOT NULL DEFAULT 'text',
|
||||
trigger_message TEXT,
|
||||
request_body TEXT,
|
||||
enabled TINYINT(1) NOT NULL DEFAULT 1,
|
||||
next_run_time DATETIME,
|
||||
last_run_time DATETIME,
|
||||
create_time DATETIME NOT NULL,
|
||||
update_time DATETIME NOT NULL,
|
||||
deleted INT NOT NULL DEFAULT 0
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
|
||||
|
||||
-- 渠道会话存储表
|
||||
CREATE TABLE IF NOT EXISTS mate_channel_session (
|
||||
id BIGINT NOT NULL PRIMARY KEY,
|
||||
conversation_id VARCHAR(128) NOT NULL UNIQUE,
|
||||
channel_type VARCHAR(32) NOT NULL,
|
||||
target_id VARCHAR(512) NOT NULL,
|
||||
sender_id VARCHAR(128),
|
||||
sender_name VARCHAR(128),
|
||||
channel_id BIGINT,
|
||||
last_active_time DATETIME NOT NULL,
|
||||
create_time DATETIME NOT NULL,
|
||||
update_time DATETIME NOT NULL,
|
||||
deleted INT NOT NULL DEFAULT 0,
|
||||
INDEX idx_channel_session_type (channel_type),
|
||||
INDEX idx_channel_session_channel_id (channel_id)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
|
||||
|
||||
-- 工作区文件表(Agent 级 Markdown 文档管理)
|
||||
CREATE TABLE IF NOT EXISTS mate_workspace_file (
|
||||
id BIGINT NOT NULL PRIMARY KEY,
|
||||
agent_id BIGINT NOT NULL,
|
||||
filename VARCHAR(256) NOT NULL,
|
||||
content LONGTEXT,
|
||||
file_size BIGINT NOT NULL DEFAULT 0,
|
||||
enabled TINYINT(1) NOT NULL DEFAULT 0,
|
||||
sort_order INT NOT NULL DEFAULT 0,
|
||||
create_time DATETIME NOT NULL,
|
||||
update_time DATETIME NOT NULL,
|
||||
deleted INT NOT NULL DEFAULT 0,
|
||||
INDEX idx_workspace_file_agent (agent_id),
|
||||
INDEX idx_workspace_file_agent_enabled (agent_id, enabled)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
|
||||
|
||||
-- ==================== MCP Server 管理 ====================
|
||||
|
||||
CREATE TABLE IF NOT EXISTS mate_mcp_server (
|
||||
id BIGINT NOT NULL PRIMARY KEY,
|
||||
name VARCHAR(128) NOT NULL,
|
||||
description TEXT,
|
||||
transport VARCHAR(32) NOT NULL DEFAULT 'stdio',
|
||||
url VARCHAR(512),
|
||||
headers_json TEXT,
|
||||
command VARCHAR(512),
|
||||
args_json TEXT,
|
||||
env_json TEXT,
|
||||
cwd VARCHAR(512),
|
||||
enabled TINYINT(1) NOT NULL DEFAULT 1,
|
||||
connect_timeout_seconds INT NOT NULL DEFAULT 30,
|
||||
read_timeout_seconds INT NOT NULL DEFAULT 30,
|
||||
last_status VARCHAR(32) NOT NULL DEFAULT 'disconnected',
|
||||
last_error TEXT,
|
||||
last_connected_time DATETIME,
|
||||
tool_count INT NOT NULL DEFAULT 0,
|
||||
builtin TINYINT(1) NOT NULL DEFAULT 0,
|
||||
create_time DATETIME NOT NULL,
|
||||
update_time DATETIME NOT NULL,
|
||||
deleted INT NOT NULL DEFAULT 0,
|
||||
INDEX idx_mcp_server_enabled (enabled)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
|
||||
|
||||
-- ==================== 工具安全治理(ToolGuard) ====================
|
||||
|
||||
-- 工具审批表
|
||||
CREATE TABLE IF NOT EXISTS mate_tool_approval (
|
||||
id BIGINT NOT NULL PRIMARY KEY,
|
||||
pending_id VARCHAR(32) NOT NULL UNIQUE,
|
||||
conversation_id VARCHAR(128) NOT NULL,
|
||||
user_id VARCHAR(64),
|
||||
agent_id VARCHAR(64),
|
||||
channel_type VARCHAR(32),
|
||||
requester_name VARCHAR(128),
|
||||
reply_target VARCHAR(512),
|
||||
tool_name VARCHAR(128) NOT NULL,
|
||||
tool_arguments TEXT,
|
||||
tool_call_payload TEXT,
|
||||
tool_call_hash VARCHAR(64),
|
||||
sibling_tool_calls TEXT,
|
||||
summary TEXT,
|
||||
findings_json TEXT,
|
||||
max_severity VARCHAR(16),
|
||||
status VARCHAR(32) NOT NULL DEFAULT 'PENDING',
|
||||
resolved_by VARCHAR(64),
|
||||
created_at DATETIME NOT NULL,
|
||||
resolved_at DATETIME,
|
||||
expire_at DATETIME,
|
||||
create_time DATETIME NOT NULL,
|
||||
update_time DATETIME NOT NULL,
|
||||
deleted INT NOT NULL DEFAULT 0,
|
||||
INDEX idx_tool_approval_conv (conversation_id),
|
||||
INDEX idx_tool_approval_status (status),
|
||||
INDEX idx_tool_approval_pending_id (pending_id)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
|
||||
|
||||
-- 安全规则表
|
||||
CREATE TABLE IF NOT EXISTS mate_tool_guard_rule (
|
||||
id BIGINT NOT NULL PRIMARY KEY,
|
||||
rule_id VARCHAR(64) NOT NULL UNIQUE,
|
||||
name VARCHAR(128) NOT NULL,
|
||||
description TEXT,
|
||||
tool_name VARCHAR(128),
|
||||
param_name VARCHAR(128),
|
||||
category VARCHAR(64) NOT NULL,
|
||||
severity VARCHAR(16) NOT NULL,
|
||||
decision VARCHAR(16) NOT NULL DEFAULT 'NEEDS_APPROVAL',
|
||||
pattern VARCHAR(512) NOT NULL,
|
||||
exclude_pattern VARCHAR(512),
|
||||
remediation TEXT,
|
||||
builtin TINYINT(1) NOT NULL DEFAULT 0,
|
||||
enabled TINYINT(1) NOT NULL DEFAULT 1,
|
||||
priority INT NOT NULL DEFAULT 100,
|
||||
create_time DATETIME NOT NULL,
|
||||
update_time DATETIME NOT NULL,
|
||||
deleted INT NOT NULL DEFAULT 0
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
|
||||
|
||||
-- 安全全局配置表
|
||||
CREATE TABLE IF NOT EXISTS mate_tool_guard_config (
|
||||
id BIGINT NOT NULL PRIMARY KEY,
|
||||
enabled TINYINT(1) NOT NULL DEFAULT 1,
|
||||
guard_scope VARCHAR(32) NOT NULL DEFAULT 'all',
|
||||
guarded_tools_json TEXT,
|
||||
denied_tools_json TEXT,
|
||||
file_guard_enabled TINYINT(1) NOT NULL DEFAULT 1,
|
||||
sensitive_paths_json TEXT,
|
||||
audit_enabled TINYINT(1) NOT NULL DEFAULT 1,
|
||||
audit_min_severity VARCHAR(16) NOT NULL DEFAULT 'INFO',
|
||||
audit_retention_days INT NOT NULL DEFAULT 90,
|
||||
create_time DATETIME NOT NULL,
|
||||
update_time DATETIME NOT NULL
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
|
||||
|
||||
-- 安全审计日志表
|
||||
CREATE TABLE IF NOT EXISTS mate_tool_guard_audit_log (
|
||||
id BIGINT NOT NULL PRIMARY KEY,
|
||||
conversation_id VARCHAR(128),
|
||||
agent_id VARCHAR(64),
|
||||
user_id VARCHAR(64),
|
||||
channel_type VARCHAR(32),
|
||||
tool_name VARCHAR(128) NOT NULL,
|
||||
tool_params_json TEXT,
|
||||
decision VARCHAR(16) NOT NULL,
|
||||
max_severity VARCHAR(16),
|
||||
findings_json TEXT,
|
||||
pending_id VARCHAR(32),
|
||||
replay_payload_hash VARCHAR(64),
|
||||
create_time DATETIME NOT NULL,
|
||||
update_time DATETIME NOT NULL,
|
||||
deleted INT NOT NULL DEFAULT 0,
|
||||
INDEX idx_guard_audit_conv (conversation_id),
|
||||
INDEX idx_guard_audit_time (create_time)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
|
||||
|
||||
-- ==================== 外部数据源 ====================
|
||||
|
||||
CREATE TABLE IF NOT EXISTS mate_datasource (
|
||||
id BIGINT NOT NULL PRIMARY KEY,
|
||||
name VARCHAR(128) NOT NULL,
|
||||
description VARCHAR(512),
|
||||
db_type VARCHAR(32) NOT NULL,
|
||||
host VARCHAR(256) NOT NULL,
|
||||
port INT NOT NULL,
|
||||
database_name VARCHAR(128) NOT NULL,
|
||||
username VARCHAR(128),
|
||||
password VARCHAR(512),
|
||||
extra_params VARCHAR(512),
|
||||
schema_name VARCHAR(128),
|
||||
enabled TINYINT(1) NOT NULL DEFAULT 1,
|
||||
last_test_time DATETIME,
|
||||
last_test_ok TINYINT(1),
|
||||
create_time DATETIME NOT NULL,
|
||||
update_time DATETIME NOT NULL,
|
||||
deleted INT NOT NULL DEFAULT 0
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
|
||||
|
||||
-- ==================== 异步任务(视频/图片生成等长耗时操作) ====================
|
||||
|
||||
CREATE TABLE IF NOT EXISTS mate_async_task (
|
||||
id BIGINT NOT NULL PRIMARY KEY,
|
||||
task_id VARCHAR(64) NOT NULL,
|
||||
task_type VARCHAR(32) NOT NULL,
|
||||
status VARCHAR(16) NOT NULL DEFAULT 'pending',
|
||||
conversation_id VARCHAR(128),
|
||||
message_id BIGINT,
|
||||
provider_name VARCHAR(64),
|
||||
provider_task_id VARCHAR(128),
|
||||
request_json TEXT,
|
||||
result_json TEXT,
|
||||
error_message VARCHAR(512),
|
||||
progress INT DEFAULT 0,
|
||||
created_by VARCHAR(64),
|
||||
create_time DATETIME NOT NULL,
|
||||
update_time DATETIME NOT NULL,
|
||||
UNIQUE KEY uk_async_task_taskid (task_id),
|
||||
INDEX idx_async_task_conv (conversation_id),
|
||||
INDEX idx_async_task_status (status)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
|
||||
|
||||
-- ==================== 记忆召回追踪 ====================
|
||||
|
||||
CREATE TABLE IF NOT EXISTS mate_memory_recall (
|
||||
id BIGINT NOT NULL PRIMARY KEY,
|
||||
agent_id BIGINT NOT NULL,
|
||||
filename VARCHAR(256) NOT NULL,
|
||||
snippet_hash VARCHAR(64),
|
||||
snippet_preview VARCHAR(512),
|
||||
recall_count INT NOT NULL DEFAULT 0,
|
||||
daily_count INT NOT NULL DEFAULT 0,
|
||||
query_hashes TEXT,
|
||||
score DOUBLE NOT NULL DEFAULT 0.0,
|
||||
last_recalled_at DATETIME,
|
||||
promoted TINYINT(1) NOT NULL DEFAULT 0,
|
||||
create_time DATETIME NOT NULL,
|
||||
update_time DATETIME NOT NULL,
|
||||
deleted INT NOT NULL DEFAULT 0,
|
||||
INDEX idx_memory_recall_agent (agent_id),
|
||||
INDEX idx_memory_recall_agent_file (agent_id, filename),
|
||||
INDEX idx_memory_recall_score (agent_id, score),
|
||||
INDEX idx_memory_recall_candidates (agent_id, promoted, deleted)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
|
||||
|
||||
-- ==================== Wiki 知识库 ====================
|
||||
|
||||
CREATE TABLE IF NOT EXISTS mate_wiki_knowledge_base (
|
||||
id BIGINT NOT NULL PRIMARY KEY,
|
||||
name VARCHAR(128) NOT NULL,
|
||||
description TEXT,
|
||||
agent_id BIGINT,
|
||||
config_content LONGTEXT,
|
||||
source_directory VARCHAR(512),
|
||||
status VARCHAR(32) NOT NULL DEFAULT 'active',
|
||||
page_count INT NOT NULL DEFAULT 0,
|
||||
raw_count INT NOT NULL DEFAULT 0,
|
||||
workspace_id BIGINT NOT NULL DEFAULT 1,
|
||||
create_time DATETIME NOT NULL,
|
||||
update_time DATETIME NOT NULL,
|
||||
deleted INT NOT NULL DEFAULT 0,
|
||||
INDEX idx_wiki_kb_agent (agent_id)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
|
||||
|
||||
CREATE TABLE IF NOT EXISTS mate_wiki_raw_material (
|
||||
id BIGINT NOT NULL PRIMARY KEY,
|
||||
kb_id BIGINT NOT NULL,
|
||||
title VARCHAR(256) NOT NULL,
|
||||
source_type VARCHAR(32) NOT NULL DEFAULT 'text',
|
||||
source_path VARCHAR(512),
|
||||
original_content LONGTEXT,
|
||||
extracted_text LONGTEXT,
|
||||
content_hash VARCHAR(64),
|
||||
file_size BIGINT NOT NULL DEFAULT 0,
|
||||
processing_status VARCHAR(32) NOT NULL DEFAULT 'pending',
|
||||
last_processed_at DATETIME,
|
||||
error_message VARCHAR(512),
|
||||
create_time DATETIME NOT NULL,
|
||||
update_time DATETIME NOT NULL,
|
||||
deleted INT NOT NULL DEFAULT 0,
|
||||
INDEX idx_wiki_raw_kb (kb_id),
|
||||
INDEX idx_wiki_raw_status (kb_id, processing_status)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
|
||||
|
||||
CREATE TABLE IF NOT EXISTS mate_wiki_page (
|
||||
id BIGINT NOT NULL PRIMARY KEY,
|
||||
kb_id BIGINT NOT NULL,
|
||||
slug VARCHAR(256) NOT NULL,
|
||||
title VARCHAR(256) NOT NULL,
|
||||
content LONGTEXT,
|
||||
summary VARCHAR(1024),
|
||||
outgoing_links LONGTEXT,
|
||||
source_raw_ids LONGTEXT,
|
||||
version INT NOT NULL DEFAULT 1,
|
||||
last_updated_by VARCHAR(32) NOT NULL DEFAULT 'ai',
|
||||
create_time DATETIME NOT NULL,
|
||||
update_time DATETIME NOT NULL,
|
||||
deleted INT NOT NULL DEFAULT 0,
|
||||
UNIQUE KEY uk_wiki_page_kb_slug (kb_id, slug),
|
||||
INDEX idx_wiki_page_kb (kb_id)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
|
||||
|
||||
-- =============================================
|
||||
-- 工作区表(Phase 2)
|
||||
-- =============================================
|
||||
|
||||
-- 工作区
|
||||
CREATE TABLE IF NOT EXISTS mate_workspace (
|
||||
id BIGINT NOT NULL PRIMARY KEY,
|
||||
name VARCHAR(128) NOT NULL,
|
||||
slug VARCHAR(64) NOT NULL,
|
||||
description VARCHAR(256),
|
||||
owner_id BIGINT,
|
||||
settings_json TEXT,
|
||||
base_path VARCHAR(512),
|
||||
create_time DATETIME NOT NULL,
|
||||
update_time DATETIME NOT NULL,
|
||||
deleted INT NOT NULL DEFAULT 0,
|
||||
UNIQUE KEY uk_workspace_slug (slug)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
|
||||
|
||||
-- 工作区成员
|
||||
CREATE TABLE IF NOT EXISTS mate_workspace_member (
|
||||
id BIGINT NOT NULL PRIMARY KEY,
|
||||
workspace_id BIGINT NOT NULL,
|
||||
user_id BIGINT NOT NULL,
|
||||
role VARCHAR(32) NOT NULL DEFAULT 'member',
|
||||
create_time DATETIME NOT NULL,
|
||||
update_time DATETIME NOT NULL,
|
||||
deleted INT NOT NULL DEFAULT 0,
|
||||
INDEX idx_ws_member_workspace (workspace_id),
|
||||
INDEX idx_ws_member_user (user_id)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
|
||||
|
||||
-- =============================================
|
||||
-- Agent-Skill / Agent-Tool 绑定表(Phase 3 Sprint 2)
|
||||
-- =============================================
|
||||
|
||||
CREATE TABLE IF NOT EXISTS mate_agent_skill (
|
||||
id BIGINT NOT NULL PRIMARY KEY,
|
||||
agent_id BIGINT NOT NULL,
|
||||
skill_id BIGINT NOT NULL,
|
||||
enabled TINYINT(1) NOT NULL DEFAULT 1,
|
||||
config_json TEXT,
|
||||
create_time DATETIME NOT NULL,
|
||||
update_time DATETIME NOT NULL,
|
||||
deleted INT NOT NULL DEFAULT 0,
|
||||
UNIQUE KEY uk_agent_skill (agent_id, skill_id)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
|
||||
|
||||
CREATE TABLE IF NOT EXISTS mate_agent_tool (
|
||||
id BIGINT NOT NULL PRIMARY KEY,
|
||||
agent_id BIGINT NOT NULL,
|
||||
tool_name VARCHAR(128) NOT NULL,
|
||||
enabled TINYINT(1) NOT NULL DEFAULT 1,
|
||||
create_time DATETIME NOT NULL,
|
||||
update_time DATETIME NOT NULL,
|
||||
deleted INT NOT NULL DEFAULT 0,
|
||||
UNIQUE KEY uk_agent_tool (agent_id, tool_name)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
|
||||
|
||||
-- =============================================
|
||||
-- CronJob 执行历史(Phase 3 Sprint 3)
|
||||
-- =============================================
|
||||
CREATE TABLE IF NOT EXISTS mate_cron_job_run (
|
||||
id BIGINT NOT NULL PRIMARY KEY,
|
||||
cron_job_id BIGINT NOT NULL,
|
||||
conversation_id VARCHAR(64),
|
||||
status VARCHAR(32) NOT NULL,
|
||||
trigger_type VARCHAR(32) NOT NULL DEFAULT 'scheduled',
|
||||
started_at DATETIME NOT NULL,
|
||||
finished_at DATETIME,
|
||||
error_message TEXT,
|
||||
token_usage INT DEFAULT 0,
|
||||
create_time DATETIME NOT NULL,
|
||||
INDEX idx_cron_run_job (cron_job_id, started_at)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
|
||||
|
||||
CREATE TABLE IF NOT EXISTS mate_usage_daily (
|
||||
id BIGINT NOT NULL PRIMARY KEY,
|
||||
workspace_id BIGINT NOT NULL,
|
||||
agent_id BIGINT,
|
||||
stat_date DATE NOT NULL,
|
||||
conversation_count INT DEFAULT 0,
|
||||
message_count INT DEFAULT 0,
|
||||
total_tokens BIGINT DEFAULT 0,
|
||||
prompt_tokens BIGINT DEFAULT 0,
|
||||
completion_tokens BIGINT DEFAULT 0,
|
||||
tool_call_count INT DEFAULT 0,
|
||||
error_count INT DEFAULT 0,
|
||||
create_time DATETIME NOT NULL,
|
||||
UNIQUE KEY uk_usage_daily (workspace_id, agent_id, stat_date)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
|
||||
|
||||
-- =============================================
|
||||
-- 操作审计事件表(Phase 3 Sprint 1)
|
||||
-- =============================================
|
||||
CREATE TABLE IF NOT EXISTS mate_audit_event (
|
||||
id BIGINT NOT NULL PRIMARY KEY,
|
||||
workspace_id BIGINT,
|
||||
user_id BIGINT NOT NULL,
|
||||
username VARCHAR(64) NOT NULL,
|
||||
action VARCHAR(64) NOT NULL,
|
||||
resource_type VARCHAR(64) NOT NULL,
|
||||
resource_id VARCHAR(128),
|
||||
resource_name VARCHAR(256),
|
||||
detail_json TEXT,
|
||||
ip_address VARCHAR(64),
|
||||
user_agent VARCHAR(256),
|
||||
create_time DATETIME NOT NULL,
|
||||
INDEX idx_audit_ws_time (workspace_id, create_time),
|
||||
INDEX idx_audit_user (user_id),
|
||||
INDEX idx_audit_resource (resource_type, resource_id)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
|
||||
|
||||
-- 清理 Codex 不支持的 ChatGPT OAuth 模型(gpt-4o, o3, o4-mini 在 Codex 模式下不可用)
|
||||
DELETE FROM mate_model_config WHERE provider = 'openai-chatgpt' AND model_name IN ('gpt-4o', 'o3', 'o4-mini');
|
||||
@ -27,6 +27,7 @@ CREATE TABLE IF NOT EXISTS mate_agent (
|
||||
enabled TINYINT(1) NOT NULL DEFAULT 1,
|
||||
icon VARCHAR(256),
|
||||
tags VARCHAR(256),
|
||||
workspace_id BIGINT NOT NULL DEFAULT 1,
|
||||
create_time DATETIME NOT NULL,
|
||||
update_time DATETIME NOT NULL,
|
||||
deleted INT NOT NULL DEFAULT 0
|
||||
@ -103,6 +104,7 @@ CREATE TABLE IF NOT EXISTS mate_skill (
|
||||
enabled TINYINT(1) NOT NULL DEFAULT 1,
|
||||
builtin TINYINT(1) NOT NULL DEFAULT 0,
|
||||
tags VARCHAR(256),
|
||||
workspace_id BIGINT NOT NULL DEFAULT 1,
|
||||
create_time DATETIME NOT NULL,
|
||||
update_time DATETIME NOT NULL,
|
||||
deleted INT NOT NULL DEFAULT 0
|
||||
@ -121,6 +123,7 @@ CREATE TABLE IF NOT EXISTS mate_tool (
|
||||
params_schema TEXT,
|
||||
enabled TINYINT(1) NOT NULL DEFAULT 1,
|
||||
builtin TINYINT(1) NOT NULL DEFAULT 0,
|
||||
workspace_id BIGINT NOT NULL DEFAULT 1,
|
||||
create_time DATETIME NOT NULL,
|
||||
update_time DATETIME NOT NULL,
|
||||
deleted INT NOT NULL DEFAULT 0
|
||||
@ -136,6 +139,7 @@ CREATE TABLE IF NOT EXISTS mate_channel (
|
||||
config_json TEXT,
|
||||
enabled TINYINT(1) NOT NULL DEFAULT 0,
|
||||
description VARCHAR(256),
|
||||
workspace_id BIGINT NOT NULL DEFAULT 1,
|
||||
create_time DATETIME NOT NULL,
|
||||
update_time DATETIME NOT NULL,
|
||||
deleted INT NOT NULL DEFAULT 0
|
||||
@ -152,6 +156,7 @@ CREATE TABLE IF NOT EXISTS mate_conversation (
|
||||
last_message TEXT,
|
||||
last_active_time DATETIME,
|
||||
stream_status VARCHAR(16) NOT NULL DEFAULT 'idle',
|
||||
workspace_id BIGINT NOT NULL DEFAULT 1,
|
||||
create_time DATETIME NOT NULL,
|
||||
update_time DATETIME NOT NULL,
|
||||
deleted INT NOT NULL DEFAULT 0,
|
||||
@ -176,7 +181,8 @@ CREATE TABLE IF NOT EXISTS mate_message (
|
||||
create_time DATETIME NOT NULL,
|
||||
update_time DATETIME NOT NULL,
|
||||
deleted INT NOT NULL DEFAULT 0,
|
||||
INDEX idx_message_conversation (conversation_id)
|
||||
INDEX idx_message_conversation (conversation_id),
|
||||
INDEX idx_message_conv_time (conversation_id, create_time)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
|
||||
|
||||
-- 执行计划表
|
||||
@ -258,7 +264,8 @@ CREATE TABLE IF NOT EXISTS mate_workspace_file (
|
||||
create_time DATETIME NOT NULL,
|
||||
update_time DATETIME NOT NULL,
|
||||
deleted INT NOT NULL DEFAULT 0,
|
||||
INDEX idx_workspace_file_agent (agent_id)
|
||||
INDEX idx_workspace_file_agent (agent_id),
|
||||
INDEX idx_workspace_file_agent_enabled (agent_id, enabled)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
|
||||
|
||||
-- ==================== MCP Server 管理 ====================
|
||||
@ -460,6 +467,7 @@ CREATE TABLE IF NOT EXISTS mate_wiki_knowledge_base (
|
||||
status VARCHAR(32) NOT NULL DEFAULT 'active',
|
||||
page_count INT NOT NULL DEFAULT 0,
|
||||
raw_count INT NOT NULL DEFAULT 0,
|
||||
workspace_id BIGINT NOT NULL DEFAULT 1,
|
||||
create_time DATETIME NOT NULL,
|
||||
update_time DATETIME NOT NULL,
|
||||
deleted INT NOT NULL DEFAULT 0,
|
||||
@ -516,6 +524,7 @@ CREATE TABLE IF NOT EXISTS mate_workspace (
|
||||
description VARCHAR(256),
|
||||
owner_id BIGINT,
|
||||
settings_json TEXT,
|
||||
base_path VARCHAR(512),
|
||||
create_time DATETIME NOT NULL,
|
||||
update_time DATETIME NOT NULL,
|
||||
deleted INT NOT NULL DEFAULT 0,
|
||||
@ -535,62 +544,32 @@ CREATE TABLE IF NOT EXISTS mate_workspace_member (
|
||||
INDEX idx_ws_member_user (user_id)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
|
||||
|
||||
-- 现有表增加 workspace_id 列(幂等)
|
||||
-- MySQL 不支持 ADD COLUMN IF NOT EXISTS,使用存储过程处理
|
||||
DROP PROCEDURE IF EXISTS mate_add_workspace_id;
|
||||
DELIMITER $$
|
||||
CREATE PROCEDURE mate_add_workspace_id()
|
||||
BEGIN
|
||||
IF NOT EXISTS (SELECT 1 FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_NAME='mate_agent' AND COLUMN_NAME='workspace_id') THEN
|
||||
ALTER TABLE mate_agent ADD COLUMN workspace_id BIGINT NOT NULL DEFAULT 1;
|
||||
END IF;
|
||||
IF NOT EXISTS (SELECT 1 FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_NAME='mate_channel' AND COLUMN_NAME='workspace_id') THEN
|
||||
ALTER TABLE mate_channel ADD COLUMN workspace_id BIGINT NOT NULL DEFAULT 1;
|
||||
END IF;
|
||||
IF NOT EXISTS (SELECT 1 FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_NAME='mate_conversation' AND COLUMN_NAME='workspace_id') THEN
|
||||
ALTER TABLE mate_conversation ADD COLUMN workspace_id BIGINT NOT NULL DEFAULT 1;
|
||||
END IF;
|
||||
IF NOT EXISTS (SELECT 1 FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_NAME='mate_wiki_knowledge_base' AND COLUMN_NAME='workspace_id') THEN
|
||||
ALTER TABLE mate_wiki_knowledge_base ADD COLUMN workspace_id BIGINT NOT NULL DEFAULT 1;
|
||||
END IF;
|
||||
IF NOT EXISTS (SELECT 1 FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_NAME='mate_tool' AND COLUMN_NAME='workspace_id') THEN
|
||||
ALTER TABLE mate_tool ADD COLUMN workspace_id BIGINT NOT NULL DEFAULT 1;
|
||||
END IF;
|
||||
IF NOT EXISTS (SELECT 1 FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_NAME='mate_skill' AND COLUMN_NAME='workspace_id') THEN
|
||||
ALTER TABLE mate_skill ADD COLUMN workspace_id BIGINT NOT NULL DEFAULT 1;
|
||||
END IF;
|
||||
END$$
|
||||
DELIMITER ;
|
||||
|
||||
CALL mate_add_workspace_id();
|
||||
DROP PROCEDURE IF EXISTS mate_add_workspace_id;
|
||||
|
||||
-- =============================================
|
||||
-- Agent-Skill / Agent-Tool 绑定表(Phase 3 Sprint 2)
|
||||
-- =============================================
|
||||
|
||||
CREATE TABLE IF NOT EXISTS mate_agent_skill (
|
||||
id BIGINT NOT NULL PRIMARY KEY,
|
||||
agent_id BIGINT NOT NULL,
|
||||
skill_id BIGINT NOT NULL,
|
||||
enabled BOOLEAN NOT NULL DEFAULT TRUE,
|
||||
id BIGINT NOT NULL PRIMARY KEY,
|
||||
agent_id BIGINT NOT NULL,
|
||||
skill_id BIGINT NOT NULL,
|
||||
enabled TINYINT(1) NOT NULL DEFAULT 1,
|
||||
config_json TEXT,
|
||||
create_time DATETIME NOT NULL,
|
||||
update_time DATETIME NOT NULL,
|
||||
deleted INT NOT NULL DEFAULT 0,
|
||||
create_time DATETIME NOT NULL,
|
||||
update_time DATETIME NOT NULL,
|
||||
deleted INT NOT NULL DEFAULT 0,
|
||||
UNIQUE KEY uk_agent_skill (agent_id, skill_id)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
|
||||
|
||||
CREATE TABLE IF NOT EXISTS mate_agent_tool (
|
||||
id BIGINT NOT NULL PRIMARY KEY,
|
||||
agent_id BIGINT NOT NULL,
|
||||
id BIGINT NOT NULL PRIMARY KEY,
|
||||
agent_id BIGINT NOT NULL,
|
||||
tool_name VARCHAR(128) NOT NULL,
|
||||
enabled BOOLEAN NOT NULL DEFAULT TRUE,
|
||||
create_time DATETIME NOT NULL,
|
||||
update_time DATETIME NOT NULL,
|
||||
deleted INT NOT NULL DEFAULT 0,
|
||||
enabled TINYINT(1) NOT NULL DEFAULT 1,
|
||||
create_time DATETIME NOT NULL,
|
||||
update_time DATETIME NOT NULL,
|
||||
deleted INT NOT NULL DEFAULT 0,
|
||||
UNIQUE KEY uk_agent_tool (agent_id, tool_name)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
|
||||
|
||||
-- =============================================
|
||||
-- CronJob 执行历史(Phase 3 Sprint 3)
|
||||
@ -607,7 +586,7 @@ CREATE TABLE IF NOT EXISTS mate_cron_job_run (
|
||||
token_usage INT DEFAULT 0,
|
||||
create_time DATETIME NOT NULL,
|
||||
INDEX idx_cron_run_job (cron_job_id, started_at)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
|
||||
|
||||
CREATE TABLE IF NOT EXISTS mate_usage_daily (
|
||||
id BIGINT NOT NULL PRIMARY KEY,
|
||||
@ -623,7 +602,7 @@ CREATE TABLE IF NOT EXISTS mate_usage_daily (
|
||||
error_count INT DEFAULT 0,
|
||||
create_time DATETIME NOT NULL,
|
||||
UNIQUE KEY uk_usage_daily (workspace_id, agent_id, stat_date)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
|
||||
|
||||
-- =============================================
|
||||
-- 操作审计事件表(Phase 3 Sprint 1)
|
||||
@ -644,4 +623,7 @@ CREATE TABLE IF NOT EXISTS mate_audit_event (
|
||||
INDEX idx_audit_ws_time (workspace_id, create_time),
|
||||
INDEX idx_audit_user (user_id),
|
||||
INDEX idx_audit_resource (resource_type, resource_id)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
|
||||
|
||||
-- 清理 Codex 不支持的 ChatGPT OAuth 模型(gpt-4o, o3, o4-mini 在 Codex 模式下不可用)
|
||||
DELETE FROM mate_model_config WHERE provider = 'openai-chatgpt' AND model_name IN ('gpt-4o', 'o3', 'o4-mini');
|
||||
|
||||
@ -28,6 +28,7 @@ CREATE TABLE IF NOT EXISTS mate_agent (
|
||||
enabled BOOLEAN NOT NULL DEFAULT TRUE,
|
||||
icon VARCHAR(256),
|
||||
tags VARCHAR(256),
|
||||
workspace_id BIGINT NOT NULL DEFAULT 1,
|
||||
create_time DATETIME NOT NULL,
|
||||
update_time DATETIME NOT NULL,
|
||||
deleted INT NOT NULL DEFAULT 0
|
||||
@ -46,6 +47,9 @@ CREATE TABLE IF NOT EXISTS mate_model_config (
|
||||
builtin BOOLEAN NOT NULL DEFAULT TRUE,
|
||||
enabled BOOLEAN NOT NULL DEFAULT TRUE,
|
||||
is_default BOOLEAN NOT NULL DEFAULT FALSE,
|
||||
max_input_tokens INT DEFAULT 0,
|
||||
enable_search BOOLEAN DEFAULT FALSE,
|
||||
search_strategy VARCHAR(32) DEFAULT NULL,
|
||||
create_time DATETIME NOT NULL,
|
||||
update_time DATETIME NOT NULL,
|
||||
deleted INT NOT NULL DEFAULT 0
|
||||
@ -66,6 +70,11 @@ CREATE TABLE IF NOT EXISTS mate_model_provider (
|
||||
support_connection_check BOOLEAN NOT NULL DEFAULT FALSE,
|
||||
freeze_url BOOLEAN NOT NULL DEFAULT FALSE,
|
||||
require_api_key BOOLEAN NOT NULL DEFAULT TRUE,
|
||||
auth_type VARCHAR(16) NOT NULL DEFAULT 'api_key',
|
||||
oauth_access_token TEXT,
|
||||
oauth_refresh_token TEXT,
|
||||
oauth_expires_at BIGINT,
|
||||
oauth_account_id VARCHAR(128),
|
||||
create_time DATETIME NOT NULL,
|
||||
update_time DATETIME NOT NULL
|
||||
);
|
||||
@ -80,11 +89,6 @@ CREATE TABLE IF NOT EXISTS mate_system_setting (
|
||||
update_time DATETIME NOT NULL
|
||||
);
|
||||
|
||||
ALTER TABLE mate_model_config ADD COLUMN IF NOT EXISTS builtin BOOLEAN NOT NULL DEFAULT TRUE;
|
||||
ALTER TABLE mate_model_config ADD COLUMN IF NOT EXISTS max_input_tokens INT DEFAULT 0;
|
||||
ALTER TABLE mate_model_config ADD COLUMN IF NOT EXISTS enable_search BOOLEAN DEFAULT FALSE;
|
||||
ALTER TABLE mate_model_config ADD COLUMN IF NOT EXISTS search_strategy VARCHAR(32) DEFAULT NULL;
|
||||
|
||||
-- 技能表
|
||||
CREATE TABLE IF NOT EXISTS mate_skill (
|
||||
id BIGINT NOT NULL PRIMARY KEY,
|
||||
@ -100,6 +104,7 @@ CREATE TABLE IF NOT EXISTS mate_skill (
|
||||
enabled BOOLEAN NOT NULL DEFAULT TRUE,
|
||||
builtin BOOLEAN NOT NULL DEFAULT FALSE,
|
||||
tags VARCHAR(256),
|
||||
workspace_id BIGINT NOT NULL DEFAULT 1,
|
||||
create_time DATETIME NOT NULL,
|
||||
update_time DATETIME NOT NULL,
|
||||
deleted INT NOT NULL DEFAULT 0
|
||||
@ -118,6 +123,7 @@ CREATE TABLE IF NOT EXISTS mate_tool (
|
||||
params_schema TEXT,
|
||||
enabled BOOLEAN NOT NULL DEFAULT TRUE,
|
||||
builtin BOOLEAN NOT NULL DEFAULT FALSE,
|
||||
workspace_id BIGINT NOT NULL DEFAULT 1,
|
||||
create_time DATETIME NOT NULL,
|
||||
update_time DATETIME NOT NULL,
|
||||
deleted INT NOT NULL DEFAULT 0
|
||||
@ -133,6 +139,7 @@ CREATE TABLE IF NOT EXISTS mate_channel (
|
||||
config_json TEXT,
|
||||
enabled BOOLEAN NOT NULL DEFAULT FALSE,
|
||||
description VARCHAR(256),
|
||||
workspace_id BIGINT NOT NULL DEFAULT 1,
|
||||
create_time DATETIME NOT NULL,
|
||||
update_time DATETIME NOT NULL,
|
||||
deleted INT NOT NULL DEFAULT 0
|
||||
@ -149,6 +156,7 @@ CREATE TABLE IF NOT EXISTS mate_conversation (
|
||||
last_message TEXT,
|
||||
last_active_time DATETIME,
|
||||
stream_status VARCHAR(16) NOT NULL DEFAULT 'idle',
|
||||
workspace_id BIGINT NOT NULL DEFAULT 1,
|
||||
create_time DATETIME NOT NULL,
|
||||
update_time DATETIME NOT NULL,
|
||||
deleted INT NOT NULL DEFAULT 0
|
||||
@ -404,9 +412,6 @@ CREATE TABLE IF NOT EXISTS mate_datasource (
|
||||
deleted INT NOT NULL DEFAULT 0
|
||||
);
|
||||
|
||||
-- 为现有表添加 metadata 列(向后兼容,防止迁移时数据丢失)
|
||||
ALTER TABLE mate_message ADD COLUMN IF NOT EXISTS metadata JSON;
|
||||
|
||||
CREATE INDEX IF NOT EXISTS idx_guard_audit_conv ON mate_tool_guard_audit_log(conversation_id);
|
||||
CREATE INDEX IF NOT EXISTS idx_guard_audit_time ON mate_tool_guard_audit_log(create_time);
|
||||
|
||||
@ -437,13 +442,6 @@ CREATE INDEX IF NOT EXISTS idx_memory_recall_candidates ON mate_memory_recall(ag
|
||||
CREATE INDEX IF NOT EXISTS idx_message_conv_time ON mate_message(conversation_id, create_time);
|
||||
CREATE INDEX IF NOT EXISTS idx_workspace_file_agent_enabled ON mate_workspace_file(agent_id, enabled);
|
||||
|
||||
-- ==================== OAuth 支持 ====================
|
||||
ALTER TABLE mate_model_provider ADD COLUMN IF NOT EXISTS auth_type VARCHAR(16) NOT NULL DEFAULT 'api_key';
|
||||
ALTER TABLE mate_model_provider ADD COLUMN IF NOT EXISTS oauth_access_token TEXT;
|
||||
ALTER TABLE mate_model_provider ADD COLUMN IF NOT EXISTS oauth_refresh_token TEXT;
|
||||
ALTER TABLE mate_model_provider ADD COLUMN IF NOT EXISTS oauth_expires_at BIGINT;
|
||||
ALTER TABLE mate_model_provider ADD COLUMN IF NOT EXISTS oauth_account_id VARCHAR(128);
|
||||
|
||||
-- 清理 Codex 不支持的 ChatGPT OAuth 模型(gpt-4o, o3, o4-mini 在 Codex 模式下不可用)
|
||||
DELETE FROM mate_model_config WHERE provider = 'openai-chatgpt' AND model_name IN ('gpt-4o', 'o3', 'o4-mini');
|
||||
|
||||
@ -467,7 +465,6 @@ CREATE TABLE IF NOT EXISTS mate_async_task (
|
||||
update_time DATETIME NOT NULL
|
||||
);
|
||||
|
||||
CREATE INDEX IF NOT EXISTS idx_async_task_taskid ON mate_async_task(task_id);
|
||||
CREATE INDEX IF NOT EXISTS idx_async_task_conv ON mate_async_task(conversation_id);
|
||||
CREATE INDEX IF NOT EXISTS idx_async_task_status ON mate_async_task(status);
|
||||
|
||||
@ -483,6 +480,7 @@ CREATE TABLE IF NOT EXISTS mate_wiki_knowledge_base (
|
||||
status VARCHAR(32) NOT NULL DEFAULT 'active',
|
||||
page_count INT NOT NULL DEFAULT 0,
|
||||
raw_count INT NOT NULL DEFAULT 0,
|
||||
workspace_id BIGINT NOT NULL DEFAULT 1,
|
||||
create_time DATETIME NOT NULL,
|
||||
update_time DATETIME NOT NULL,
|
||||
deleted INT NOT NULL DEFAULT 0
|
||||
@ -539,6 +537,7 @@ CREATE TABLE IF NOT EXISTS mate_workspace (
|
||||
description VARCHAR(256),
|
||||
owner_id BIGINT,
|
||||
settings_json TEXT,
|
||||
base_path VARCHAR(512),
|
||||
create_time DATETIME NOT NULL,
|
||||
update_time DATETIME NOT NULL,
|
||||
deleted INT NOT NULL DEFAULT 0,
|
||||
@ -558,14 +557,6 @@ CREATE TABLE IF NOT EXISTS mate_workspace_member (
|
||||
CREATE INDEX IF NOT EXISTS idx_ws_member_workspace ON mate_workspace_member(workspace_id);
|
||||
CREATE INDEX IF NOT EXISTS idx_ws_member_user ON mate_workspace_member(user_id);
|
||||
|
||||
-- 现有表增加 workspace_id 列
|
||||
ALTER TABLE mate_agent ADD COLUMN IF NOT EXISTS workspace_id BIGINT NOT NULL DEFAULT 1;
|
||||
ALTER TABLE mate_channel ADD COLUMN IF NOT EXISTS workspace_id BIGINT NOT NULL DEFAULT 1;
|
||||
ALTER TABLE mate_conversation ADD COLUMN IF NOT EXISTS workspace_id BIGINT NOT NULL DEFAULT 1;
|
||||
ALTER TABLE mate_wiki_knowledge_base ADD COLUMN IF NOT EXISTS workspace_id BIGINT NOT NULL DEFAULT 1;
|
||||
ALTER TABLE mate_tool ADD COLUMN IF NOT EXISTS workspace_id BIGINT NOT NULL DEFAULT 1;
|
||||
ALTER TABLE mate_skill ADD COLUMN IF NOT EXISTS workspace_id BIGINT NOT NULL DEFAULT 1;
|
||||
|
||||
-- =============================================
|
||||
-- Agent-Skill / Agent-Tool 绑定表(Phase 3 Sprint 2)
|
||||
-- =============================================
|
||||
|
||||
@ -752,6 +752,7 @@ export default {
|
||||
name: 'Name',
|
||||
slug: 'Slug',
|
||||
description: 'Description',
|
||||
basePath: 'Working Directory',
|
||||
created: 'Created',
|
||||
actions: 'Actions',
|
||||
},
|
||||
@ -764,6 +765,9 @@ export default {
|
||||
slugHint: 'Slug cannot be changed after creation.',
|
||||
description: 'Description',
|
||||
descriptionPlaceholder: 'Optional description',
|
||||
basePath: 'Working Directory',
|
||||
basePathPlaceholder: 'e.g. /home/user/project or D:\\codes\\MyProject',
|
||||
basePathHint: 'Restrict agent file access to this directory. Leave empty for no restriction.',
|
||||
},
|
||||
editDialog: {
|
||||
title: 'Edit Workspace',
|
||||
|
||||
@ -752,6 +752,7 @@ export default {
|
||||
name: '名称',
|
||||
slug: '标识',
|
||||
description: '描述',
|
||||
basePath: '活动目录',
|
||||
created: '创建时间',
|
||||
actions: '操作',
|
||||
},
|
||||
@ -764,6 +765,9 @@ export default {
|
||||
slugHint: '标识创建后不可修改。',
|
||||
description: '描述',
|
||||
descriptionPlaceholder: '可选描述',
|
||||
basePath: '活动目录',
|
||||
basePathPlaceholder: '例如:/home/user/project 或 D:\\codes\\MyProject',
|
||||
basePathHint: '限制 Agent 文件操作的根目录,留空不限制。',
|
||||
},
|
||||
editDialog: {
|
||||
title: '编辑工作区',
|
||||
|
||||
@ -7,6 +7,7 @@ export interface Workspace {
|
||||
name: string
|
||||
slug: string
|
||||
description?: string
|
||||
basePath?: string
|
||||
ownerId?: number
|
||||
settingsJson?: string
|
||||
createTime?: string
|
||||
|
||||
@ -23,6 +23,7 @@
|
||||
<th>{{ t('security.workspaces.columns.name') }}</th>
|
||||
<th>{{ t('security.workspaces.columns.slug') }}</th>
|
||||
<th>{{ t('security.workspaces.columns.description') }}</th>
|
||||
<th>{{ t('security.workspaces.columns.basePath') }}</th>
|
||||
<th>{{ t('security.workspaces.columns.created') }}</th>
|
||||
<th style="width: 120px;">{{ t('security.workspaces.columns.actions') }}</th>
|
||||
</tr>
|
||||
@ -37,6 +38,7 @@
|
||||
</td>
|
||||
<td class="slug-cell">{{ ws.slug }}</td>
|
||||
<td class="desc-cell">{{ ws.description || '-' }}</td>
|
||||
<td class="slug-cell">{{ ws.basePath || '-' }}</td>
|
||||
<td class="date-cell">{{ formatDate(ws.createTime) }}</td>
|
||||
<td>
|
||||
<div class="action-btns">
|
||||
@ -93,6 +95,11 @@
|
||||
<label>{{ t('security.workspaces.createDialog.description') }}</label>
|
||||
<input v-model="form.description" class="form-input" :placeholder="t('security.workspaces.createDialog.descriptionPlaceholder')" />
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label>{{ t('security.workspaces.createDialog.basePath') }}</label>
|
||||
<input v-model="form.basePath" class="form-input mono" :placeholder="t('security.workspaces.createDialog.basePathPlaceholder')" />
|
||||
<span class="form-hint">{{ t('security.workspaces.createDialog.basePathHint') }}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
@ -150,6 +157,7 @@ const form = ref({
|
||||
name: '',
|
||||
slug: '',
|
||||
description: '',
|
||||
basePath: '',
|
||||
})
|
||||
|
||||
onMounted(() => {
|
||||
@ -170,7 +178,7 @@ async function fetchWorkspaces() {
|
||||
|
||||
function openCreateDialog() {
|
||||
editingWs.value = null
|
||||
form.value = { name: '', slug: '', description: '' }
|
||||
form.value = { name: '', slug: '', description: '', basePath: '' }
|
||||
showDialog.value = true
|
||||
}
|
||||
|
||||
@ -180,6 +188,7 @@ function openEditDialog(ws: Workspace) {
|
||||
name: ws.name,
|
||||
slug: ws.slug,
|
||||
description: ws.description || '',
|
||||
basePath: ws.basePath || '',
|
||||
}
|
||||
showDialog.value = true
|
||||
}
|
||||
@ -199,12 +208,14 @@ async function saveWorkspace() {
|
||||
await workspaceTeamApi.update(editingWs.value.id, {
|
||||
name: form.value.name,
|
||||
description: form.value.description,
|
||||
basePath: form.value.basePath || null,
|
||||
})
|
||||
} else {
|
||||
await workspaceTeamApi.create({
|
||||
name: form.value.name,
|
||||
slug: form.value.slug,
|
||||
description: form.value.description,
|
||||
basePath: form.value.basePath || null,
|
||||
})
|
||||
}
|
||||
showDialog.value = false
|
||||
|
||||
Loading…
Reference in New Issue
Block a user