flowmind 1.2.3 → 1.3.0

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.
@@ -0,0 +1,79 @@
1
+ /**
2
+ * Code Review Skill
3
+ * Analyzes code for security vulnerabilities, style violations, and best practices
4
+ */
5
+
6
+ const fs = require('fs-extra');
7
+ const path = require('path');
8
+
9
+ const SECURITY_PATTERNS = [
10
+ { pattern: /(\bselect\b.*\bfrom\b.*\bwhere\b.*['"]\s*\+\s*)/i, name: 'SQL Injection', severity: 'HIGH' },
11
+ { pattern: /eval\s*\(/, name: 'Code Injection (eval)', severity: 'HIGH' },
12
+ { pattern: /innerHTML\s*=/, name: 'XSS (innerHTML)', severity: 'MEDIUM' },
13
+ { pattern: /password\s*=\s*['"][^'"]+['"]/i, name: 'Hardcoded Password', severity: 'HIGH' },
14
+ { pattern: /api[_-]?key\s*=\s*['"][^'"]+['"]/i, name: 'Hardcoded API Key', severity: 'HIGH' },
15
+ { pattern: /secret\s*=\s*['"][^'"]+['"]/i, name: 'Hardcoded Secret', severity: 'HIGH' },
16
+ { pattern: /md5\s*\(/i, name: 'Weak Crypto (MD5)', severity: 'MEDIUM' },
17
+ { pattern: /exec\s*\(\s*['"`]/, name: 'Command Injection', severity: 'HIGH' },
18
+ ];
19
+
20
+ const QUALITY_PATTERNS = [
21
+ { pattern: /console\.(log|debug|info)\s*\(/, name: 'Console statement', severity: 'LOW' },
22
+ { pattern: /\/\/\s*TODO/i, name: 'TODO comment', severity: 'LOW' },
23
+ { pattern: /\/\/\s*FIXME/i, name: 'FIXME comment', severity: 'MEDIUM' },
24
+ { pattern: /\/\/\s*HACK/i, name: 'HACK comment', severity: 'MEDIUM' },
25
+ { pattern: /catch\s*\(\s*\w*\s*\)\s*\{\s*\}/, name: 'Empty catch block', severity: 'MEDIUM' },
26
+ ];
27
+
28
+ module.exports = {
29
+ canHandle(input, context) {
30
+ if (!input) return false;
31
+ const lower = input.toLowerCase();
32
+ return /代码审查|code review|review code|检查代码|分析代码/.test(lower);
33
+ },
34
+
35
+ async execute(input, context) {
36
+ const issues = [];
37
+ const filePath = extractFilePath(input);
38
+
39
+ if (filePath && await fs.pathExists(filePath)) {
40
+ const content = await fs.readFile(filePath, 'utf-8');
41
+ const lines = content.split('\n');
42
+ const relPath = path.basename(filePath);
43
+
44
+ lines.forEach((line, idx) => {
45
+ SECURITY_PATTERNS.forEach(({ pattern, name, severity }) => {
46
+ if (pattern.test(line)) {
47
+ issues.push({ file: relPath, line: idx + 1, type: 'security', name, severity, code: line.trim() });
48
+ }
49
+ });
50
+ QUALITY_PATTERNS.forEach(({ pattern, name, severity }) => {
51
+ if (pattern.test(line)) {
52
+ issues.push({ file: relPath, line: idx + 1, type: 'quality', name, severity, code: line.trim() });
53
+ }
54
+ });
55
+ });
56
+ }
57
+
58
+ const high = issues.filter(i => i.severity === 'HIGH').length;
59
+ const medium = issues.filter(i => i.severity === 'MEDIUM').length;
60
+ const low = issues.filter(i => i.severity === 'LOW').length;
61
+
62
+ return {
63
+ type: 'result',
64
+ skill: 'code-review',
65
+ message: issues.length > 0
66
+ ? `Found ${issues.length} issue(s): ${high} HIGH, ${medium} MEDIUM, ${low} LOW`
67
+ : 'No issues found',
68
+ data: { issues, summary: { total: issues.length, high, medium, low } },
69
+ input,
70
+ timestamp: new Date().toISOString()
71
+ };
72
+ }
73
+ };
74
+
75
+ function extractFilePath(input) {
76
+ const match = input.match(/(?:review|审查|检查|分析)\s+(.+?\.[a-zA-Z]+)$/i)
77
+ || input.match(/([^\s]+\.(js|ts|jsx|tsx|py|java|go|rs|c|cpp|rb))$/i);
78
+ return match ? match[1].trim() : null;
79
+ }
@@ -0,0 +1,77 @@
1
+ /**
2
+ * Code Review Audit Skill
3
+ * Three-dimensional review: security audit, design compliance, mandatory constraints
4
+ */
5
+
6
+ const fs = require('fs-extra');
7
+
8
+ const SECURITY_CHECKS = [
9
+ { pattern: /(\bselect\b.*\bfrom\b.*\bwhere\b.*['"]\s*\+\s*)/i, name: 'SQL Injection', severity: 'HIGH' },
10
+ { pattern: /eval\s*\(/, name: 'Code Injection', severity: 'HIGH' },
11
+ { pattern: /innerHTML\s*=/, name: 'XSS Risk', severity: 'MEDIUM' },
12
+ { pattern: /password|secret|apikey|token/i, name: 'Potential Sensitive Data', severity: 'MEDIUM' },
13
+ ];
14
+
15
+ const CONSTRAINT_CHECKS = [
16
+ { pattern: /class\s+[a-z]/, name: 'Class naming (should be PascalCase)', severity: 'LOW' },
17
+ { pattern: /function\s+[A-Z]/, name: 'Function naming (should be camelCase)', severity: 'LOW' },
18
+ { pattern: /catch\s*\(\s*\w*\s*\)\s*\{\s*\}/, name: 'Empty catch block', severity: 'MEDIUM' },
19
+ { pattern: /\/\/\s*TODO/i, name: 'TODO comment', severity: 'LOW' },
20
+ ];
21
+
22
+ module.exports = {
23
+ canHandle(input, context) {
24
+ if (!input) return false;
25
+ return /安全审计|security audit|design compliance|设计合规|code review audit|审查审计/i.test(input);
26
+ },
27
+
28
+ async execute(input, context) {
29
+ const findings = [];
30
+ const filePath = extractFilePath(input);
31
+
32
+ if (filePath && await fs.pathExists(filePath)) {
33
+ const content = await fs.readFile(filePath, 'utf-8');
34
+ const lines = content.split('\n');
35
+
36
+ lines.forEach((line, idx) => {
37
+ SECURITY_CHECKS.forEach(({ pattern, name, severity }) => {
38
+ if (pattern.test(line)) {
39
+ findings.push({ dimension: 'security', line: idx + 1, name, severity, code: line.trim() });
40
+ }
41
+ });
42
+ CONSTRAINT_CHECKS.forEach(({ pattern, name, severity }) => {
43
+ if (pattern.test(line)) {
44
+ findings.push({ dimension: 'constraint', line: idx + 1, name, severity, code: line.trim() });
45
+ }
46
+ });
47
+ });
48
+ }
49
+
50
+ const high = findings.filter(f => f.severity === 'HIGH').length;
51
+ const medium = findings.filter(f => f.severity === 'MEDIUM').length;
52
+ const verdict = high > 0 ? 'FAIL' : medium > 0 ? 'CONDITIONAL' : 'PASS';
53
+
54
+ return {
55
+ type: 'result',
56
+ skill: 'code-review-audit',
57
+ message: `Audit verdict: ${verdict} (${findings.length} findings)`,
58
+ data: {
59
+ verdict,
60
+ findings,
61
+ summary: { total: findings.length, high, medium, low: findings.filter(f => f.severity === 'LOW').length },
62
+ dimensions: {
63
+ security: findings.filter(f => f.dimension === 'security').length,
64
+ constraint: findings.filter(f => f.dimension === 'constraint').length
65
+ }
66
+ },
67
+ input,
68
+ timestamp: new Date().toISOString()
69
+ };
70
+ }
71
+ };
72
+
73
+ function extractFilePath(input) {
74
+ const match = input.match(/(?:audit|审计|审查)\s+(.+?\.[a-zA-Z]+)$/i)
75
+ || input.match(/([^\s]+\.(js|ts|jsx|tsx|py|java|go))$/i);
76
+ return match ? match[1].trim() : null;
77
+ }
@@ -0,0 +1,108 @@
1
+ /**
2
+ * Data Logic Validation Skill
3
+ * Verify SQL queries, Redis operations, and data processing logic against real data via MCP
4
+ */
5
+
6
+ module.exports = {
7
+ canHandle(input, context) {
8
+ if (!input) return false;
9
+ return /数据逻辑|data.*logic.*valid|sql.*验证|redis.*验证|数据.*校验.*逻辑/i.test(input);
10
+ },
11
+
12
+ async execute(input, context) {
13
+ const registry = context.componentRegistry;
14
+ const params = parseValidationParams(input);
15
+
16
+ // Check MCP component availability
17
+ const dbManager = registry?.getAdapter('databaseManager');
18
+ const dbQuery = registry?.getAdapter('databaseQuery');
19
+ const redisMonitor = registry?.getAdapter('redisMonitor');
20
+
21
+ const availableComponents = {
22
+ databaseManager: !!dbManager,
23
+ databaseQuery: !!dbQuery,
24
+ redisMonitor: !!redisMonitor
25
+ };
26
+
27
+ if (params.action === 'sql') {
28
+ if (!dbQuery) {
29
+ return {
30
+ type: 'result',
31
+ skill: 'data-logic-validation',
32
+ message: 'Database query service not configured (need friday-rds-redis-query MCP)',
33
+ data: { availableComponents, hint: 'Configure database query MCP server first' },
34
+ input,
35
+ timestamp: new Date().toISOString()
36
+ };
37
+ }
38
+
39
+ return {
40
+ type: 'result',
41
+ skill: 'data-logic-validation',
42
+ message: `SQL validation ready for: ${params.query || 'provided query'}`,
43
+ data: {
44
+ action: 'validate_sql',
45
+ query: params.query,
46
+ mcpTool: 'mcpQueryExec',
47
+ availableComponents
48
+ },
49
+ input,
50
+ timestamp: new Date().toISOString()
51
+ };
52
+ }
53
+
54
+ if (params.action === 'redis') {
55
+ if (!redisMonitor) {
56
+ return {
57
+ type: 'result',
58
+ skill: 'data-logic-validation',
59
+ message: 'Redis monitor not configured (need friday-aliyun-sz-rds-redis MCP)',
60
+ data: { availableComponents, hint: 'Configure Redis monitor MCP server first' },
61
+ input,
62
+ timestamp: new Date().toISOString()
63
+ };
64
+ }
65
+
66
+ return {
67
+ type: 'result',
68
+ skill: 'data-logic-validation',
69
+ message: `Redis validation ready for key: ${params.key || 'provided key'}`,
70
+ data: {
71
+ action: 'validate_redis',
72
+ key: params.key,
73
+ mcpTools: ['mcpRedisKeyGet', 'mcpRedisKeyInfo', 'mcpRedisKeys'],
74
+ availableComponents
75
+ },
76
+ input,
77
+ timestamp: new Date().toISOString()
78
+ };
79
+ }
80
+
81
+ return {
82
+ type: 'result',
83
+ skill: 'data-logic-validation',
84
+ message: 'Data logic validation. Available: sql, redis',
85
+ data: {
86
+ actions: ['sql - Validate SQL queries', 'redis - Validate Redis operations'],
87
+ availableComponents,
88
+ requiredMCP: ['databaseManager', 'databaseQuery', 'redisMonitor']
89
+ },
90
+ input,
91
+ timestamp: new Date().toISOString()
92
+ };
93
+ }
94
+ };
95
+
96
+ function parseValidationParams(input) {
97
+ const params = {};
98
+ if (/sql|查询|query|select|insert|update|delete/i.test(input)) params.action = 'sql';
99
+ if (/redis|缓存|cache|key/i.test(input)) params.action = 'redis';
100
+
101
+ const sqlMatch = input.match(/(?:sql|查询)\s*[:=]?\s*(select\s+.+?)(?:\s*$)/i);
102
+ if (sqlMatch) params.query = sqlMatch[1].trim();
103
+
104
+ const keyMatch = input.match(/(?:key|键)\s*[:=]?\s*(\S+)/i);
105
+ if (keyMatch) params.key = keyMatch[1];
106
+
107
+ return params;
108
+ }
@@ -0,0 +1,72 @@
1
+ /**
2
+ * Data Validation Skill
3
+ * Validates data against schema rules and business logic
4
+ */
5
+
6
+ module.exports = {
7
+ canHandle(input, context) {
8
+ if (!input) return false;
9
+ return /数据校验|数据验证|data valid|validate data|校验数据|检查数据/i.test(input);
10
+ },
11
+
12
+ async execute(input, context) {
13
+ const data = extractData(input);
14
+
15
+ if (!data) {
16
+ return {
17
+ type: 'result',
18
+ skill: 'data-validation',
19
+ message: 'No data found to validate. Provide JSON data or a file path.',
20
+ input,
21
+ timestamp: new Date().toISOString()
22
+ };
23
+ }
24
+
25
+ const issues = [];
26
+
27
+ // Schema validation
28
+ if (data.rules && data.values) {
29
+ for (const [field, rule] of Object.entries(data.rules)) {
30
+ const value = data.values[field];
31
+
32
+ if (rule.required && (value === undefined || value === null || value === '')) {
33
+ issues.push({ field, rule: 'required', message: `${field} is required but missing`, severity: 'ERROR' });
34
+ }
35
+ if (value !== undefined && rule.type && typeof value !== rule.type) {
36
+ issues.push({ field, rule: 'type', message: `${field} should be ${rule.type}, got ${typeof value}`, severity: 'ERROR' });
37
+ }
38
+ if (value !== undefined && rule.min !== undefined && value < rule.min) {
39
+ issues.push({ field, rule: 'min', message: `${field} (${value}) below minimum (${rule.min})`, severity: 'WARNING' });
40
+ }
41
+ if (value !== undefined && rule.max !== undefined && value > rule.max) {
42
+ issues.push({ field, rule: 'max', message: `${field} (${value}) above maximum (${rule.max})`, severity: 'WARNING' });
43
+ }
44
+ if (value !== undefined && rule.pattern && !new RegExp(rule.pattern).test(String(value))) {
45
+ issues.push({ field, rule: 'pattern', message: `${field} doesn't match pattern`, severity: 'WARNING' });
46
+ }
47
+ }
48
+ }
49
+
50
+ const errors = issues.filter(i => i.severity === 'ERROR').length;
51
+ const warnings = issues.filter(i => i.severity === 'WARNING').length;
52
+
53
+ return {
54
+ type: 'result',
55
+ skill: 'data-validation',
56
+ message: issues.length > 0
57
+ ? `Validation: ${errors} errors, ${warnings} warnings`
58
+ : 'All validations passed',
59
+ data: { issues, summary: { errors, warnings, passed: issues.length === 0 } },
60
+ input,
61
+ timestamp: new Date().toISOString()
62
+ };
63
+ }
64
+ };
65
+
66
+ function extractData(input) {
67
+ const jsonMatch = input.match(/\{[\s\S]*\}/);
68
+ if (jsonMatch) {
69
+ try { return JSON.parse(jsonMatch[0]); } catch {}
70
+ }
71
+ return null;
72
+ }
@@ -0,0 +1,73 @@
1
+ /**
2
+ * Git Review Skill
3
+ * Reviews commit history, analyzes changes, ensures code quality
4
+ */
5
+
6
+ const { execSync } = require('child_process');
7
+
8
+ module.exports = {
9
+ canHandle(input, context) {
10
+ if (!input) return false;
11
+ return /git\s*review|commit.*review|代码提交|提交审查|review.*commit|检查提交/i.test(input);
12
+ },
13
+
14
+ async execute(input, context) {
15
+ const count = extractCount(input) || 5;
16
+
17
+ try {
18
+ const logRaw = execSync(
19
+ `git log --oneline -${count} --format="%H|%s|%an|%ai"`,
20
+ { encoding: 'utf-8', timeout: 10000 }
21
+ ).trim();
22
+
23
+ if (!logRaw) {
24
+ return { type: 'result', skill: 'git-review', message: 'No commits found', input, timestamp: new Date().toISOString() };
25
+ }
26
+
27
+ const commits = logRaw.split('\n').map(line => {
28
+ const [hash, subject, author, date] = line.split('|');
29
+ return { hash: hash?.slice(0, 8), subject, author, date };
30
+ });
31
+
32
+ const issues = [];
33
+ const good = [];
34
+
35
+ commits.forEach(c => {
36
+ // Check conventional commit format
37
+ const conventional = /^(feat|fix|docs|style|refactor|test|chore|perf|ci|build)(\(.+\))?: .+/;
38
+ if (!conventional.test(c.subject)) {
39
+ issues.push({ hash: c.hash, type: 'format', message: `Non-conventional commit message: "${c.subject}"` });
40
+ } else {
41
+ good.push({ hash: c.hash, message: c.subject });
42
+ }
43
+
44
+ // Check length
45
+ if (c.subject && c.subject.length > 72) {
46
+ issues.push({ hash: c.hash, type: 'length', message: `Subject too long (${c.subject.length} chars)` });
47
+ }
48
+ });
49
+
50
+ return {
51
+ type: 'result',
52
+ skill: 'git-review',
53
+ message: `Reviewed ${commits.length} commits: ${good.length} good, ${issues.length} issues`,
54
+ data: { commits, issues, good, summary: { total: commits.length, good: good.length, issues: issues.length } },
55
+ input,
56
+ timestamp: new Date().toISOString()
57
+ };
58
+ } catch (e) {
59
+ return {
60
+ type: 'error',
61
+ skill: 'git-review',
62
+ message: `Git review failed: ${e.message}`,
63
+ input,
64
+ timestamp: new Date().toISOString()
65
+ };
66
+ }
67
+ }
68
+ };
69
+
70
+ function extractCount(input) {
71
+ const match = input.match(/(\d+)\s*(?:个|条|commits?|次)/i) || input.match(/-(\d+)/);
72
+ return match ? parseInt(match[1]) : null;
73
+ }
@@ -0,0 +1,50 @@
1
+ /**
2
+ * Learning Engine Skill
3
+ * Delegates to core LearningEngine for detecting and recording learning patterns
4
+ */
5
+
6
+ module.exports = {
7
+ canHandle(input, context) {
8
+ if (!input) return false;
9
+ const lower = input.toLowerCase();
10
+ const patterns = [
11
+ /不[对是]/, /错[了误]/, /应该[是用]/, /改[成为]/, /优化/, /改进/,
12
+ /记住/, /学习/, /纠正/, /prefer/, /learn/, /correct/, /remember/
13
+ ];
14
+ return patterns.some(p => p.test(lower));
15
+ },
16
+
17
+ async execute(input, context) {
18
+ const learning = context.flowmind?.learning;
19
+ if (!learning) {
20
+ return {
21
+ type: 'error',
22
+ skill: 'learning-engine',
23
+ message: 'Learning engine not available',
24
+ input
25
+ };
26
+ }
27
+
28
+ const result = await learning.detectLearning(input, context);
29
+
30
+ if (result) {
31
+ return {
32
+ type: 'learning',
33
+ skill: 'learning-engine',
34
+ learningType: result.type,
35
+ message: result.message || `Detected ${result.type} learning pattern`,
36
+ data: result,
37
+ input,
38
+ timestamp: new Date().toISOString()
39
+ };
40
+ }
41
+
42
+ return {
43
+ type: 'result',
44
+ skill: 'learning-engine',
45
+ message: 'No learning pattern detected in input',
46
+ input,
47
+ timestamp: new Date().toISOString()
48
+ };
49
+ }
50
+ };
@@ -0,0 +1,83 @@
1
+ /**
2
+ * Learning Feedback Skill
3
+ * Captures user corrections, feedback, and refinement suggestions.
4
+ * Overlaps with learning-engine but focuses on feedback capture and binding.
5
+ */
6
+
7
+ module.exports = {
8
+ canHandle(input, context) {
9
+ if (!input) return false;
10
+ const lower = input.toLowerCase();
11
+ const feedbackPatterns = [
12
+ /不对/, /错了/, /应该是/, /不要/, /别这样/,
13
+ /下次/, /以后/, /记得/, /别再/, /改成/,
14
+ /wrong/, /should be/, /don't/, /next time/, /change to/
15
+ ];
16
+ return feedbackPatterns.some(p => p.test(lower));
17
+ },
18
+
19
+ async execute(input, context) {
20
+ const learning = context.flowmind?.learning;
21
+ if (!learning) {
22
+ return {
23
+ type: 'error',
24
+ skill: 'learning-feedback',
25
+ message: 'Learning engine not available',
26
+ input
27
+ };
28
+ }
29
+
30
+ // Detect correction patterns
31
+ const correction = learning.detectCorrection(input);
32
+ if (correction) {
33
+ const record = await learning.recordCorrection(correction, context);
34
+ return {
35
+ type: 'learning',
36
+ skill: 'learning-feedback',
37
+ learningType: 'correction',
38
+ message: `Recorded correction: ${correction.summary || 'user feedback captured'}`,
39
+ data: record,
40
+ input,
41
+ timestamp: new Date().toISOString()
42
+ };
43
+ }
44
+
45
+ // Detect preference patterns
46
+ const preference = learning.detectPreference(input);
47
+ if (preference) {
48
+ const record = await learning.recordPreference(preference, context);
49
+ return {
50
+ type: 'learning',
51
+ skill: 'learning-feedback',
52
+ learningType: 'preference',
53
+ message: `Recorded preference: ${preference.type} = ${preference.value}`,
54
+ data: record,
55
+ input,
56
+ timestamp: new Date().toISOString()
57
+ };
58
+ }
59
+
60
+ // Detect scene mapping
61
+ const scene = learning.detectSceneMapping(input);
62
+ if (scene) {
63
+ const record = await learning.recordSceneMapping(scene, context);
64
+ return {
65
+ type: 'learning',
66
+ skill: 'learning-feedback',
67
+ learningType: 'scene_mapping',
68
+ message: `Recorded scene mapping for skill: ${scene.skill}`,
69
+ data: record,
70
+ input,
71
+ timestamp: new Date().toISOString()
72
+ };
73
+ }
74
+
75
+ return {
76
+ type: 'result',
77
+ skill: 'learning-feedback',
78
+ message: 'No feedback pattern detected',
79
+ input,
80
+ timestamp: new Date().toISOString()
81
+ };
82
+ }
83
+ };
@@ -0,0 +1,88 @@
1
+ /**
2
+ * Log Audit Skill
3
+ * Analyzes application logs, traces requests, debugs performance issues
4
+ */
5
+
6
+ module.exports = {
7
+ canHandle(input, context) {
8
+ if (!input) return false;
9
+ return /日志分析|log.*audit|日志查询|查日志|log.*analy|trace.*id|调用链/i.test(input);
10
+ },
11
+
12
+ async execute(input, context) {
13
+ const logService = context.componentRegistry?.getAdapter('logService');
14
+ const params = parseLogParams(input);
15
+
16
+ if (!logService && !params.mock) {
17
+ return {
18
+ type: 'result',
19
+ skill: 'log-audit',
20
+ message: 'Log service not configured. Connect an SLS/ELK log service first.',
21
+ data: { params, hint: 'Use `flowmind resource` to configure log service connections' },
22
+ input,
23
+ timestamp: new Date().toISOString()
24
+ };
25
+ }
26
+
27
+ // If we have a log service, format the query for MCP tools
28
+ if (params.traceId) {
29
+ return {
30
+ type: 'result',
31
+ skill: 'log-audit',
32
+ message: `Trace query for: ${params.traceId}`,
33
+ data: {
34
+ action: 'trace',
35
+ traceId: params.traceId,
36
+ query: `* and "${params.traceId}"`,
37
+ timeRange: params.timeRange || 'last 1 hour',
38
+ endpoint: params.endpoint || 'cn-shenzhen.log.aliyuncs.com'
39
+ },
40
+ input,
41
+ timestamp: new Date().toISOString()
42
+ };
43
+ }
44
+
45
+ return {
46
+ type: 'result',
47
+ skill: 'log-audit',
48
+ message: `Log query: ${params.service || 'all services'}, ${params.level || 'all levels'}, ${params.timeRange || 'last 1 hour'}`,
49
+ data: {
50
+ action: 'query',
51
+ query: buildSLSQuery(params),
52
+ timeRange: params.timeRange || 'last 1 hour',
53
+ endpoint: params.endpoint || 'cn-shenzhen.log.aliyuncs.com',
54
+ limit: params.limit || 100
55
+ },
56
+ input,
57
+ timestamp: new Date().toISOString()
58
+ };
59
+ }
60
+ };
61
+
62
+ function parseLogParams(input) {
63
+ const params = {};
64
+ const traceMatch = input.match(/(?:trace[_-]?id|调用链)\s*[:=]?\s*(\S+)/i);
65
+ if (traceMatch) params.traceId = traceMatch[1];
66
+
67
+ const serviceMatch = input.match(/(?:服务|service)\s*[:=]?\s*(\S+)/i);
68
+ if (serviceMatch) params.service = serviceMatch[1];
69
+
70
+ const levelMatch = input.match(/(?:级别|level)\s*[:=]?\s*(ERROR|WARN|INFO|DEBUG)/i);
71
+ if (levelMatch) params.level = levelMatch[1].toUpperCase();
72
+
73
+ const timeMatch = input.match(/(?:最近|last)\s*(\d+)\s*(分钟|小时|分钟|min|hour)/i);
74
+ if (timeMatch) params.timeRange = `last ${timeMatch[1]} ${timeMatch[2]}`;
75
+
76
+ const keywordMatch = input.match(/(?:关键词|keyword|搜索|search)\s*[:=]?\s*(.+?)(?:\s*$)/i);
77
+ if (keywordMatch) params.keyword = keywordMatch[1].trim();
78
+
79
+ return params;
80
+ }
81
+
82
+ function buildSLSQuery(params) {
83
+ const parts = [];
84
+ if (params.service) parts.push(`service: ${params.service}`);
85
+ if (params.level) parts.push(`level: ${params.level}`);
86
+ if (params.keyword) parts.push(`"${params.keyword}"`);
87
+ return parts.length > 0 ? parts.join(' and ') : '*';
88
+ }