@eoasmxd/freya 0.4.0 → 0.4.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (57) hide show
  1. package/core/dist/agent/agent-executor.js +5 -0
  2. package/core/dist/command/command-executor.js +7 -0
  3. package/core/dist/command/command-registry.d.ts +6 -1
  4. package/core/dist/command/command-registry.js +31 -1
  5. package/core/dist/command/commands/skill-commands.js +5 -4
  6. package/core/dist/config/config-manager.d.ts +5 -1
  7. package/core/dist/config/config-manager.js +48 -1
  8. package/core/dist/kernel.js +4 -4
  9. package/core/dist/skill/skill-registry.d.ts +12 -2
  10. package/core/dist/skill/skill-registry.js +89 -8
  11. package/core/dist/tools/meta/index.d.ts +3 -1
  12. package/core/dist/tools/meta/index.js +9 -1
  13. package/core/dist/tools/tool-registry.d.ts +12 -7
  14. package/core/dist/tools/tool-registry.js +44 -8
  15. package/core/dist/web/config-api.js +18 -0
  16. package/core/package.json +2 -2
  17. package/package.json +4 -3
  18. package/plugins/plugin-gemini/package.json +1 -1
  19. package/plugins/plugin-openai/package.json +1 -1
  20. package/plugins/plugin-telegram-channel/package.json +1 -1
  21. package/plugins/plugin-tool-fs/package.json +1 -1
  22. package/plugins/plugin-tool-memory/package.json +1 -1
  23. package/plugins/plugin-tool-mysql/config/prompts/plugin.prompt.mysql.md +9 -0
  24. package/plugins/plugin-tool-mysql/config/prompts/plugin.prompt.mysql.select.audit.md +26 -0
  25. package/plugins/plugin-tool-mysql/dist/audit.d.ts +13 -0
  26. package/plugins/plugin-tool-mysql/dist/audit.js +87 -0
  27. package/plugins/plugin-tool-mysql/dist/index.d.ts +14 -0
  28. package/plugins/plugin-tool-mysql/dist/index.js +34 -0
  29. package/plugins/plugin-tool-mysql/dist/pool-manager.d.ts +32 -0
  30. package/plugins/plugin-tool-mysql/dist/pool-manager.js +113 -0
  31. package/plugins/plugin-tool-mysql/dist/tools.d.ts +11 -0
  32. package/plugins/plugin-tool-mysql/dist/tools.js +88 -0
  33. package/plugins/plugin-tool-mysql/package.json +33 -0
  34. package/plugins/plugin-tool-mysql/schema.json +83 -0
  35. package/plugins/plugin-tool-web/package.json +1 -1
  36. package/plugins/plugin-wecom-channel/package.json +1 -1
  37. package/plugins/plugin-weixin-channel/dist/index.js +10 -36
  38. package/plugins/plugin-weixin-channel/package.json +3 -2
  39. package/src/packages/core/src/agent/agent-executor.ts +5 -0
  40. package/src/packages/core/src/command/command-executor.ts +8 -0
  41. package/src/packages/core/src/command/command-registry.ts +35 -1
  42. package/src/packages/core/src/command/commands/skill-commands.ts +6 -5
  43. package/src/packages/core/src/config/config-manager.ts +50 -1
  44. package/src/packages/core/src/kernel.ts +5 -4
  45. package/src/packages/core/src/skill/skill-registry.ts +100 -8
  46. package/src/packages/core/src/tools/meta/index.ts +9 -1
  47. package/src/packages/core/src/tools/tool-registry.ts +49 -9
  48. package/src/packages/core/src/web/config-api.ts +20 -0
  49. package/src/packages/ui/src/features/config/ConfigModal.tsx +13 -1
  50. package/src/packages/ui/src/features/config/panels/SkillConfigPanel.tsx +137 -0
  51. package/src/plugins/plugin-tool-mysql/src/audit.ts +103 -0
  52. package/src/plugins/plugin-tool-mysql/src/index.ts +44 -0
  53. package/src/plugins/plugin-tool-mysql/src/pool-manager.ts +132 -0
  54. package/src/plugins/plugin-tool-mysql/src/tools.ts +100 -0
  55. package/src/plugins/plugin-weixin-channel/src/index.ts +12 -37
  56. package/ui/assets/{index-Be0cAgdB.js → index-BqPQMflk.js} +14 -14
  57. package/ui/index.html +1 -1
@@ -18,7 +18,7 @@
18
18
  "clean": "rm -rf dist"
19
19
  },
20
20
  "dependencies": {
21
- "@eoasmxd/freya-sdk": "^0.4.0"
21
+ "@eoasmxd/freya-sdk": "^0.4.2"
22
22
  },
23
23
  "devDependencies": {
24
24
  "@types/node": "^22.0.0",
@@ -18,7 +18,7 @@
18
18
  "clean": "rm -rf dist"
19
19
  },
20
20
  "dependencies": {
21
- "@eoasmxd/freya-sdk": "^0.4.0"
21
+ "@eoasmxd/freya-sdk": "^0.4.2"
22
22
  },
23
23
  "devDependencies": {
24
24
  "@types/node": "^22.0.0",
@@ -19,7 +19,7 @@
19
19
  "clean": "rm -rf dist"
20
20
  },
21
21
  "dependencies": {
22
- "@eoasmxd/freya-sdk": "^0.4.0"
22
+ "@eoasmxd/freya-sdk": "^0.4.2"
23
23
  },
24
24
  "devDependencies": {
25
25
  "@types/node": "^22.0.0",
@@ -21,7 +21,7 @@
21
21
  "clean": "rm -rf dist"
22
22
  },
23
23
  "dependencies": {
24
- "@eoasmxd/freya-sdk": "^0.4.0"
24
+ "@eoasmxd/freya-sdk": "^0.4.2"
25
25
  },
26
26
  "devDependencies": {
27
27
  "@types/node": "^22.0.0",
@@ -21,7 +21,7 @@
21
21
  "clean": "rm -rf dist"
22
22
  },
23
23
  "dependencies": {
24
- "@eoasmxd/freya-sdk": "^0.4.0"
24
+ "@eoasmxd/freya-sdk": "^0.4.2"
25
25
  },
26
26
  "devDependencies": {
27
27
  "@types/node": "^22.0.0",
@@ -0,0 +1,9 @@
1
+ MySQL 数据库查询工具箱。
2
+
3
+ 提供执行标准 SQL 只读查询语句并返回结构化 JSON 数据结果的能力。
4
+
5
+ 使用规范:
6
+ 1. 本工具仅支持执行 SELECT 查询语句,严禁执行任何非 SELECT 语句,禁止执行任何增删改或 DDL 破坏性语句。
7
+ 2. 严禁主观猜测表名与字段名,必须严格基于上下文或业务 Skill 指南中确定、已知的数据结构编写 SQL。
8
+ 3. 支持通过 `connection` 参数指定目标数据库连接名称,若不传则自动使用默认连接。
9
+ 4. 单次查询返回结果条数受到系统安全限制截断,编写查询时建议显式使用合理的 `LIMIT` 子句。
@@ -0,0 +1,26 @@
1
+ 你是一个严格的 SQL 安全与完整性审查审计员。你的唯一职责是对待执行的 SQL 查询语句进行安全与完整性审查。
2
+
3
+ 审查准则:
4
+ 1. **完整性审查**:
5
+ - SQL 语句必须语法结构完整、语义明确。
6
+ - 严禁包含伪代码、截断残缺语句或未闭合的括号/引号。
7
+
8
+ 2. **只读安全约束**:
9
+ - 必须仅为 SELECT 查询操作(仅允许 SELECT 查询语句,严禁执行 SHOW、EXPLAIN、DESCRIBE 等其他语句)。
10
+ - 严禁包含任何数据修改语句(如 INSERT、UPDATE、DELETE、REPLACE 等)。
11
+ - 严禁包含任何数据定义与删除语句(如 DROP、TRUNCATE、ALTER、CREATE 等)。
12
+ - 严禁包含权限管理、事务控制等系统级命令(如 GRANT、REVOKE、LOCK TABLES 等)。
13
+ - 严禁利用分号 `;` 拼接多条执行语句进行批处理或 SQL 注入攻击。
14
+ - 严禁包含高危文件读写或外部系统交互指令(如 INTO OUTFILE、INTO DUMPFILE、LOAD DATA、LOAD_FILE 等)。
15
+
16
+ 输出规范:
17
+ 请直接返回合法且不包含 Markdown 代码块标记(如 ```json)的纯 JSON 字符串,格式如下:
18
+ {
19
+ "passed": true,
20
+ "reason": "审核通过,为合法的只读查询语句。"
21
+ }
22
+
23
+ {
24
+ "passed": false,
25
+ "reason": "具体的拦截阻断原因"
26
+ }
@@ -0,0 +1,13 @@
1
+ import type { FreyaContext } from '@eoasmxd/freya-sdk';
2
+ export interface AuditResult {
3
+ passed: boolean;
4
+ reason: string;
5
+ }
6
+ /** 前置 SQL 安全与完整性审查审计服务 */
7
+ export declare class SqlAuditService {
8
+ private cachedPrompt;
9
+ /** 双通道探针加载审计提示词模板 */
10
+ private loadAuditPrompt;
11
+ /** 执行前置独立 LLM 分析审查 */
12
+ audit(sql: string, connectionName: string, ctx: FreyaContext): Promise<AuditResult>;
13
+ }
@@ -0,0 +1,87 @@
1
+ import fs from 'node:fs/promises';
2
+ import path from 'node:path';
3
+ import { fileURLToPath } from 'node:url';
4
+ /** 前置 SQL 安全与完整性审查审计服务 */
5
+ export class SqlAuditService {
6
+ cachedPrompt = null;
7
+ /** 双通道探针加载审计提示词模板 */
8
+ async loadAuditPrompt(ctx) {
9
+ if (this.cachedPrompt) {
10
+ return this.cachedPrompt;
11
+ }
12
+ const promptFileName = 'plugin.prompt.mysql.select.audit.md';
13
+ const runtimeOverridePath = path.join(ctx.paths.projectRoot, 'config', 'prompts', promptFileName);
14
+ try {
15
+ const content = await fs.readFile(runtimeOverridePath, 'utf-8');
16
+ if (content.trim()) {
17
+ this.cachedPrompt = content;
18
+ return content;
19
+ }
20
+ }
21
+ catch {
22
+ // 运行时覆盖不存在,继续降级读取内置模板
23
+ }
24
+ const currentDir = path.dirname(fileURLToPath(import.meta.url));
25
+ const packageDefaultPath = path.resolve(currentDir, '..', 'config', 'prompts', promptFileName);
26
+ try {
27
+ const content = await fs.readFile(packageDefaultPath, 'utf-8');
28
+ this.cachedPrompt = content;
29
+ return content;
30
+ }
31
+ catch (err) {
32
+ ctx.logger.error(`加载内置 SQL 审计提示词模板失败: ${packageDefaultPath}`, err);
33
+ return '';
34
+ }
35
+ }
36
+ /** 执行前置独立 LLM 分析审查 */
37
+ async audit(sql, connectionName, ctx) {
38
+ const trimmedSql = sql.trim();
39
+ if (!trimmedSql) {
40
+ return { passed: false, reason: 'SQL 语句内容不能为空。' };
41
+ }
42
+ const auditPrompt = await this.loadAuditPrompt(ctx);
43
+ if (!auditPrompt) {
44
+ return { passed: false, reason: 'SQL 审计提示词模板未就绪,安全拒绝执行。' };
45
+ }
46
+ const messages = [
47
+ {
48
+ role: 'system',
49
+ content: auditPrompt
50
+ },
51
+ {
52
+ role: 'user',
53
+ content: JSON.stringify({
54
+ connection: connectionName,
55
+ sql: trimmedSql
56
+ }, null, 2)
57
+ }
58
+ ];
59
+ try {
60
+ const response = await ctx.llm.chat(messages);
61
+ const rawOutput = response.message?.content?.trim() || '';
62
+ const cleaned = rawOutput
63
+ .replace(/^```json\s*/i, '')
64
+ .replace(/^```\s*/i, '')
65
+ .replace(/\s*```$/i, '')
66
+ .trim();
67
+ const parsed = JSON.parse(cleaned);
68
+ if (typeof parsed.passed === 'boolean') {
69
+ return {
70
+ passed: parsed.passed,
71
+ reason: parsed.reason ? String(parsed.reason) : (parsed.passed ? '审查通过' : '未提供拦截原因')
72
+ };
73
+ }
74
+ return {
75
+ passed: false,
76
+ reason: `审查响应结构不符合预期: ${rawOutput}`
77
+ };
78
+ }
79
+ catch (err) {
80
+ ctx.logger.error('前置 LLM SQL 审计过程发生异常', err);
81
+ return {
82
+ passed: false,
83
+ reason: `SQL 安全审查服务调用失败: ${err?.message || err}`
84
+ };
85
+ }
86
+ }
87
+ }
@@ -0,0 +1,14 @@
1
+ import type { FreyaContext, ToolPlugin, FreyaTool } from '@eoasmxd/freya-sdk';
2
+ /** MySQL 数据库查询工具箱插件 */
3
+ export default class MysqlToolsPlugin implements ToolPlugin {
4
+ type: "tool";
5
+ private poolManager?;
6
+ private auditService;
7
+ private tools;
8
+ setup(ctx: FreyaContext): Promise<void>;
9
+ getId(): string;
10
+ getInstructionPrompt(): string;
11
+ getTools(): FreyaTool[];
12
+ stop(ctx: FreyaContext): Promise<void>;
13
+ }
14
+ export declare const Plugin: typeof MysqlToolsPlugin;
@@ -0,0 +1,34 @@
1
+ import { MysqlPoolManager } from './pool-manager.js';
2
+ import { SqlAuditService } from './audit.js';
3
+ import { MysqlQueryTool } from './tools.js';
4
+ /** MySQL 数据库查询工具箱插件 */
5
+ export default class MysqlToolsPlugin {
6
+ type = 'tool';
7
+ poolManager;
8
+ auditService = new SqlAuditService();
9
+ tools = [];
10
+ async setup(ctx) {
11
+ this.poolManager = new MysqlPoolManager(ctx);
12
+ this.tools = [
13
+ new MysqlQueryTool(this.poolManager, this.auditService)
14
+ ];
15
+ const available = this.poolManager.getAvailableConnectionNames();
16
+ ctx.logger.info(`MySQL 工具箱插件初始化就绪,已注册连接: [${available.join(', ') || '暂未配置'}]`);
17
+ }
18
+ getId() {
19
+ return 'mysql';
20
+ }
21
+ getInstructionPrompt() {
22
+ return 'plugin.prompt.mysql';
23
+ }
24
+ getTools() {
25
+ return this.tools;
26
+ }
27
+ async stop(ctx) {
28
+ if (this.poolManager) {
29
+ await this.poolManager.closeAll();
30
+ ctx.logger.info('MySQL 工具箱连接资源已全部释放。');
31
+ }
32
+ }
33
+ }
34
+ export const Plugin = MysqlToolsPlugin;
@@ -0,0 +1,32 @@
1
+ import mysql from 'mysql2/promise';
2
+ import type { FreyaContext } from '@eoasmxd/freya-sdk';
3
+ /** MySQL 连接配置契约 */
4
+ export interface MysqlConnectionConfig {
5
+ name: string;
6
+ host?: string;
7
+ port?: number;
8
+ user?: string;
9
+ password?: string;
10
+ database?: string;
11
+ connectionLimit?: number;
12
+ connectTimeout?: number;
13
+ }
14
+ /** MySQL 多命名连接池管理器 */
15
+ export declare class MysqlPoolManager {
16
+ private ctx;
17
+ private pools;
18
+ private connectionConfigs;
19
+ private defaultConnectionName;
20
+ constructor(ctx: FreyaContext);
21
+ /** 加载与同步配置 */
22
+ reloadConfigs(): void;
23
+ /** 获取指定名称的数据库连接池 */
24
+ getPool(targetName?: string): {
25
+ pool: mysql.Pool;
26
+ config: MysqlConnectionConfig;
27
+ };
28
+ /** 获取所有可用连接名称 */
29
+ getAvailableConnectionNames(): string[];
30
+ /** 释放所有已建立的连接池资源 */
31
+ closeAll(): Promise<void>;
32
+ }
@@ -0,0 +1,113 @@
1
+ import mysql from 'mysql2/promise';
2
+ /** 安全解算嵌套或扁平配置值 */
3
+ function getNestedConfig(config, keyPath) {
4
+ if (!config)
5
+ return undefined;
6
+ if (keyPath in config)
7
+ return config[keyPath];
8
+ const parts = keyPath.split('.');
9
+ let current = config;
10
+ for (const part of parts) {
11
+ if (current && typeof current === 'object' && part in current) {
12
+ current = current[part];
13
+ }
14
+ else {
15
+ return undefined;
16
+ }
17
+ }
18
+ return current;
19
+ }
20
+ /** MySQL 多命名连接池管理器 */
21
+ export class MysqlPoolManager {
22
+ ctx;
23
+ pools = new Map();
24
+ connectionConfigs = new Map();
25
+ defaultConnectionName = 'default';
26
+ constructor(ctx) {
27
+ this.ctx = ctx;
28
+ this.reloadConfigs();
29
+ }
30
+ /** 加载与同步配置 */
31
+ reloadConfigs() {
32
+ this.connectionConfigs.clear();
33
+ const config = this.ctx.config || {};
34
+ this.defaultConnectionName = getNestedConfig(config, 'mysql.defaultConnection') || 'default';
35
+ const connections = getNestedConfig(config, 'mysql.connections');
36
+ if (Array.isArray(connections)) {
37
+ for (const item of connections) {
38
+ if (item && typeof item === 'object' && item.name) {
39
+ this.connectionConfigs.set(item.name, {
40
+ name: String(item.name),
41
+ host: item.host ? String(item.host) : '127.0.0.1',
42
+ port: item.port ? Number(item.port) : 3306,
43
+ user: item.user ? String(item.user) : 'root',
44
+ password: item.password ? String(item.password) : '',
45
+ database: item.database ? String(item.database) : undefined,
46
+ connectionLimit: item.connectionLimit ? Number(item.connectionLimit) : 5,
47
+ connectTimeout: item.connectTimeout ? Number(item.connectTimeout) : 10000
48
+ });
49
+ }
50
+ }
51
+ }
52
+ if (this.connectionConfigs.size === 0) {
53
+ const singleHost = getNestedConfig(config, 'mysql.host');
54
+ if (singleHost) {
55
+ this.connectionConfigs.set(this.defaultConnectionName, {
56
+ name: this.defaultConnectionName,
57
+ host: String(singleHost),
58
+ port: getNestedConfig(config, 'mysql.port') ? Number(getNestedConfig(config, 'mysql.port')) : 3306,
59
+ user: getNestedConfig(config, 'mysql.user') ? String(getNestedConfig(config, 'mysql.user')) : 'root',
60
+ password: getNestedConfig(config, 'mysql.password') ? String(getNestedConfig(config, 'mysql.password')) : '',
61
+ database: getNestedConfig(config, 'mysql.database') ? String(getNestedConfig(config, 'mysql.database')) : undefined,
62
+ connectionLimit: getNestedConfig(config, 'mysql.connectionLimit') ? Number(getNestedConfig(config, 'mysql.connectionLimit')) : 5,
63
+ connectTimeout: getNestedConfig(config, 'mysql.connectTimeout') ? Number(getNestedConfig(config, 'mysql.connectTimeout')) : 10000
64
+ });
65
+ }
66
+ }
67
+ }
68
+ /** 获取指定名称的数据库连接池 */
69
+ getPool(targetName) {
70
+ this.reloadConfigs();
71
+ const connectionName = targetName || this.defaultConnectionName;
72
+ const conf = this.connectionConfigs.get(connectionName);
73
+ if (!conf) {
74
+ const available = Array.from(this.connectionConfigs.keys()).join(', ') || '无配置';
75
+ throw new Error(`未找到名为 "${connectionName}" 的 MySQL 连接配置。当前可用连接: [${available}]`);
76
+ }
77
+ let pool = this.pools.get(connectionName);
78
+ if (!pool) {
79
+ pool = mysql.createPool({
80
+ host: conf.host,
81
+ port: conf.port,
82
+ user: conf.user,
83
+ password: conf.password,
84
+ database: conf.database,
85
+ connectionLimit: conf.connectionLimit,
86
+ connectTimeout: conf.connectTimeout,
87
+ waitForConnections: true,
88
+ queueLimit: 0,
89
+ dateStrings: true
90
+ });
91
+ this.pools.set(connectionName, pool);
92
+ this.ctx.logger.info(`MySQL 连接池已创建: [${connectionName}] -> ${conf.user}@${conf.host}:${conf.port}/${conf.database || ''}`);
93
+ }
94
+ return { pool, config: conf };
95
+ }
96
+ /** 获取所有可用连接名称 */
97
+ getAvailableConnectionNames() {
98
+ return Array.from(this.connectionConfigs.keys());
99
+ }
100
+ /** 释放所有已建立的连接池资源 */
101
+ async closeAll() {
102
+ for (const [name, pool] of this.pools.entries()) {
103
+ try {
104
+ await pool.end();
105
+ this.ctx.logger.info(`MySQL 连接池已释放: [${name}]`);
106
+ }
107
+ catch (err) {
108
+ this.ctx.logger.warn(`关闭 MySQL 连接池 [${name}] 时发生异常: ${err?.message || err}`);
109
+ }
110
+ }
111
+ this.pools.clear();
112
+ }
113
+ }
@@ -0,0 +1,11 @@
1
+ import type { FreyaContext, ToolDefinition, FreyaTool } from '@eoasmxd/freya-sdk';
2
+ import type { MysqlPoolManager } from './pool-manager.js';
3
+ import type { SqlAuditService } from './audit.js';
4
+ /** MySQL 数据库查询执行工具 */
5
+ export declare class MysqlQueryTool implements FreyaTool {
6
+ private poolManager;
7
+ private auditService;
8
+ constructor(poolManager: MysqlPoolManager, auditService: SqlAuditService);
9
+ getDefinition(): ToolDefinition;
10
+ execute(args: Record<string, any>, ctx: FreyaContext): Promise<string>;
11
+ }
@@ -0,0 +1,88 @@
1
+ /** 脱敏错误信息中的敏感凭据 */
2
+ function sanitizeErrorMessage(err, password) {
3
+ let message = err?.message || String(err);
4
+ if (password) {
5
+ message = message.replaceAll(password, '******');
6
+ }
7
+ return message;
8
+ }
9
+ /** MySQL 数据库查询执行工具 */
10
+ export class MysqlQueryTool {
11
+ poolManager;
12
+ auditService;
13
+ constructor(poolManager, auditService) {
14
+ this.poolManager = poolManager;
15
+ this.auditService = auditService;
16
+ }
17
+ getDefinition() {
18
+ return {
19
+ name: 'mysql_query',
20
+ description: '执行 MySQL SELECT 查询语句并返回结构化数据。执行前会进行独立 LLM 安全与完整性审核,支持通过 connection 指定目标连接。',
21
+ parameters: {
22
+ type: 'object',
23
+ properties: {
24
+ sql: {
25
+ type: 'string',
26
+ description: '待执行的 SQL 查询语句'
27
+ },
28
+ connection: {
29
+ type: 'string',
30
+ description: '目标数据库连接名称(可选,若未指定则使用系统默认连接)'
31
+ },
32
+ params: {
33
+ type: 'array',
34
+ items: {},
35
+ description: '可选的参数化查询参数列表'
36
+ }
37
+ },
38
+ required: ['sql']
39
+ }
40
+ };
41
+ }
42
+ async execute(args, ctx) {
43
+ const rawSql = typeof args.sql === 'string' ? args.sql.trim() : '';
44
+ if (!rawSql) {
45
+ return '❌ 参数错误: sql 语句不能为空。';
46
+ }
47
+ const connectionName = typeof args.connection === 'string' && args.connection.trim()
48
+ ? args.connection.trim()
49
+ : undefined;
50
+ const queryParams = Array.isArray(args.params) ? args.params : undefined;
51
+ const auditResult = await this.auditService.audit(rawSql, connectionName || 'default', ctx);
52
+ if (!auditResult.passed) {
53
+ return `❌ SQL 审查未通过,已拒绝执行。\n原因: ${auditResult.reason}`;
54
+ }
55
+ let currentPassword = '';
56
+ try {
57
+ const { pool, config } = this.poolManager.getPool(connectionName);
58
+ currentPassword = config.password || '';
59
+ const maxRows = Number(ctx.config?.mysql?.maxRows ?? ctx.config?.['mysql.maxRows']) || 100;
60
+ const [rows] = await pool.query(rawSql, queryParams);
61
+ if (!Array.isArray(rows)) {
62
+ return JSON.stringify({
63
+ connection: config.name,
64
+ database: config.database || null,
65
+ affectedRows: rows.affectedRows ?? 0,
66
+ info: rows.info || '查询已完成'
67
+ }, null, 2);
68
+ }
69
+ const totalCount = rows.length;
70
+ const truncated = totalCount > maxRows;
71
+ const data = truncated ? rows.slice(0, maxRows) : rows;
72
+ return JSON.stringify({
73
+ connection: config.name,
74
+ database: config.database || null,
75
+ totalCount,
76
+ returnedCount: data.length,
77
+ truncated,
78
+ notice: truncated ? `结果集超过最大限制 ${maxRows} 条,已自动截断返回前 ${maxRows} 条记录。` : undefined,
79
+ data
80
+ }, null, 2);
81
+ }
82
+ catch (err) {
83
+ const safeMessage = sanitizeErrorMessage(err, currentPassword);
84
+ ctx.logger.error(`MySQL 查询执行异常: ${safeMessage}`);
85
+ return `❌ MySQL 查询执行失败: ${safeMessage}`;
86
+ }
87
+ }
88
+ }
@@ -0,0 +1,33 @@
1
+ {
2
+ "name": "@eoasmxd/freya-plugin-tool-mysql",
3
+ "version": "0.1.0",
4
+ "private": false,
5
+ "license": "MIT",
6
+ "publishConfig": {
7
+ "access": "public"
8
+ },
9
+ "description": "Freya MySQL 数据库查询工具集插件",
10
+ "freya": {
11
+ "displayName": "MySQL 数据库查询工具集",
12
+ "defaultEnabled": false,
13
+ "schema": "./schema.json",
14
+ "prompts": [
15
+ "plugin.prompt.mysql.md",
16
+ "plugin.prompt.mysql.select.audit.md"
17
+ ]
18
+ },
19
+ "type": "module",
20
+ "main": "./dist/index.js",
21
+ "scripts": {
22
+ "build": "tsc -p tsconfig.json",
23
+ "clean": "rm -rf dist"
24
+ },
25
+ "dependencies": {
26
+ "@eoasmxd/freya-sdk": "^0.4.2",
27
+ "mysql2": "^3.11.0"
28
+ },
29
+ "devDependencies": {
30
+ "@types/node": "^22.0.0",
31
+ "typescript": "^5.5.0"
32
+ }
33
+ }
@@ -0,0 +1,83 @@
1
+ [
2
+ {
3
+ "key": "mysql.defaultConnection",
4
+ "defaultValue": "default",
5
+ "description": "默认使用的数据库连接名称",
6
+ "type": "string",
7
+ "category": "MySQL 连接"
8
+ },
9
+ {
10
+ "key": "mysql.maxRows",
11
+ "defaultValue": 100,
12
+ "description": "单次查询最大返回记录条数(防止大模型上下文窗口溢出)",
13
+ "type": "number",
14
+ "min": 1,
15
+ "max": 1000,
16
+ "category": "MySQL 安全",
17
+ "uiHint": "slider"
18
+ },
19
+ {
20
+ "key": "mysql.connections",
21
+ "defaultValue": [],
22
+ "description": "MySQL 命名数据库连接配置列表",
23
+ "type": "array",
24
+ "category": "MySQL 连接",
25
+ "children": [
26
+ {
27
+ "key": "name",
28
+ "description": "连接名称 (例如 default、bi_db)",
29
+ "type": "string",
30
+ "required": true
31
+ },
32
+ {
33
+ "key": "host",
34
+ "description": "数据库主机 (例如 127.0.0.1)",
35
+ "type": "string",
36
+ "required": true
37
+ },
38
+ {
39
+ "key": "port",
40
+ "description": "数据库端口 (默认 3306)",
41
+ "type": "number",
42
+ "defaultValue": 3306,
43
+ "required": true
44
+ },
45
+ {
46
+ "key": "user",
47
+ "description": "数据库用户名",
48
+ "type": "string",
49
+ "required": true
50
+ },
51
+ {
52
+ "key": "password",
53
+ "description": "数据库密码",
54
+ "type": "string",
55
+ "required": true,
56
+ "sensitive": true,
57
+ "uiHint": "password"
58
+ },
59
+ {
60
+ "key": "database",
61
+ "description": "数据库名称",
62
+ "type": "string",
63
+ "required": false
64
+ },
65
+ {
66
+ "key": "connectionLimit",
67
+ "description": "连接池最大连接数 (默认 5)",
68
+ "type": "number",
69
+ "defaultValue": 5,
70
+ "min": 1,
71
+ "max": 50
72
+ },
73
+ {
74
+ "key": "connectTimeout",
75
+ "description": "连接超时时间 (毫秒,默认 10000)",
76
+ "type": "number",
77
+ "defaultValue": 10000,
78
+ "min": 1000,
79
+ "max": 60000
80
+ }
81
+ ]
82
+ }
83
+ ]
@@ -22,7 +22,7 @@
22
22
  "clean": "rm -rf dist"
23
23
  },
24
24
  "dependencies": {
25
- "@eoasmxd/freya-sdk": "^0.4.0"
25
+ "@eoasmxd/freya-sdk": "^0.4.2"
26
26
  },
27
27
  "devDependencies": {
28
28
  "@types/node": "^22.0.0",
@@ -19,7 +19,7 @@
19
19
  "clean": "rm -rf dist"
20
20
  },
21
21
  "dependencies": {
22
- "@eoasmxd/freya-sdk": "^0.4.0",
22
+ "@eoasmxd/freya-sdk": "^0.4.2",
23
23
  "ws": "^8.18.0"
24
24
  },
25
25
  "devDependencies": {