@eoasmxd/freya 0.4.4 → 0.5.1
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.
- package/core/dist/config/config-manager.js +3 -3
- package/core/dist/config/file-handler.js +7 -7
- package/core/dist/context.js +7 -6
- package/core/dist/llm/llm-logger.js +2 -2
- package/core/dist/logger.js +2 -2
- package/core/dist/plugin/plugin-manager.d.ts +1 -1
- package/core/dist/plugin/plugin-manager.js +35 -8
- package/core/dist/prompt/prompt-manager.js +4 -4
- package/core/dist/prompt/prompt-registry.d.ts +4 -4
- package/core/dist/prompt/prompt-registry.js +34 -15
- package/core/dist/session/persistence.js +2 -2
- package/core/dist/skill/skill-registry.d.ts +1 -1
- package/core/dist/skill/skill-registry.js +18 -8
- package/core/dist/utils/paths.d.ts +5 -3
- package/core/dist/utils/paths.js +10 -6
- package/core/dist/web/web-container.js +5 -5
- package/core/package.json +2 -2
- package/doc/specifications/config-spec.md +1 -1
- package/doc/tutorials/part1_react/3.2_decoupled_architecture.md +1 -1
- package/doc/tutorials/part1_react/3.3_freya_dual_read_probe.md +1 -1
- package/doc/tutorials/part3_memory/6.2_physical_sandbox_separation.md +5 -5
- package/doc/tutorials/part5_plugins/10.1_microkernel_decoupling.md +5 -4
- package/freya.js +14 -2
- package/package.json +2 -2
- package/plugins/plugin-gemini/package.json +1 -1
- package/plugins/plugin-openai/package.json +1 -1
- package/plugins/plugin-telegram-channel/package.json +1 -1
- package/plugins/plugin-tool-fs/package.json +1 -1
- package/plugins/plugin-tool-memory/dist/tools.js +2 -2
- package/plugins/plugin-tool-memory/package.json +1 -1
- package/plugins/plugin-tool-mysql/dist/audit.js +1 -1
- package/plugins/plugin-tool-mysql/package.json +1 -1
- package/plugins/plugin-tool-web/package.json +1 -1
- package/plugins/plugin-wecom-channel/package.json +1 -1
- package/plugins/plugin-weixin-channel/package.json +1 -1
- package/src/packages/core/src/config/config-manager.ts +3 -3
- package/src/packages/core/src/config/file-handler.ts +7 -7
- package/src/packages/core/src/context.ts +7 -6
- package/src/packages/core/src/llm/llm-logger.ts +2 -2
- package/src/packages/core/src/logger.ts +2 -2
- package/src/packages/core/src/plugin/plugin-manager.ts +40 -11
- package/src/packages/core/src/prompt/prompt-manager.ts +4 -4
- package/src/packages/core/src/prompt/prompt-registry.ts +40 -16
- package/src/packages/core/src/session/persistence.ts +2 -2
- package/src/packages/core/src/skill/skill-registry.ts +24 -10
- package/src/packages/core/src/utils/paths.ts +11 -7
- package/src/packages/core/src/web/web-container.ts +5 -5
- package/src/packages/sdk/src/types/context.ts +4 -2
- package/src/packages/ui/src/features/config/panels/PluginConfigPanel.tsx +30 -0
- package/src/packages/ui/src/features/config/panels/SkillConfigPanel.tsx +17 -5
- package/src/plugins/plugin-tool-memory/src/tools.ts +2 -2
- package/src/plugins/plugin-tool-mysql/src/audit.ts +1 -1
- package/ui/assets/{index-CyDg-8Iv.js → index-CSZmaZbL.js} +15 -15
- package/ui/index.html +1 -1
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import type { FreyaContext } from '@eoasmxd/freya-sdk';
|
|
2
2
|
import fs from 'node:fs/promises';
|
|
3
3
|
import path from 'node:path';
|
|
4
|
-
import {
|
|
4
|
+
import { FREYA_APP, FREYA_HOME, FREYA_WORKSPACE } from '../utils/paths.js';
|
|
5
5
|
|
|
6
6
|
export interface FreyaSkill {
|
|
7
7
|
id: string;
|
|
@@ -9,7 +9,7 @@ export interface FreyaSkill {
|
|
|
9
9
|
description: string;
|
|
10
10
|
content: string;
|
|
11
11
|
enabled: boolean;
|
|
12
|
-
source: 'builtin' | 'runtime';
|
|
12
|
+
source: 'builtin' | 'workspace' | 'runtime';
|
|
13
13
|
}
|
|
14
14
|
|
|
15
15
|
/** 技能注册表,从 skills/ 目录加载 Markdown 格式技能并管理软开关状态 */
|
|
@@ -19,13 +19,23 @@ export class FreyaSkillRegistry {
|
|
|
19
19
|
|
|
20
20
|
async loadSkills(context: FreyaContext): Promise<void> {
|
|
21
21
|
this.context = context;
|
|
22
|
-
const
|
|
23
|
-
const
|
|
24
|
-
const
|
|
22
|
+
const defaultSkillsDir = path.join(FREYA_APP, 'skills');
|
|
23
|
+
const workspaceSkillsDir = path.join(FREYA_WORKSPACE, 'skills');
|
|
24
|
+
const runtimeSkillsDir = path.join(FREYA_HOME, 'skills');
|
|
25
|
+
const configSkillsPath = path.join(FREYA_HOME, 'config', 'skills.json');
|
|
25
26
|
|
|
26
27
|
try {
|
|
27
28
|
await fs.mkdir(runtimeSkillsDir, { recursive: true });
|
|
28
29
|
await this.loadSkillsFromDirectory(defaultSkillsDir, 'builtin', context);
|
|
30
|
+
|
|
31
|
+
const resolvedDefault = path.resolve(defaultSkillsDir);
|
|
32
|
+
const resolvedWorkspace = path.resolve(workspaceSkillsDir);
|
|
33
|
+
const resolvedRuntime = path.resolve(runtimeSkillsDir);
|
|
34
|
+
|
|
35
|
+
if (resolvedWorkspace !== resolvedDefault && resolvedWorkspace !== resolvedRuntime) {
|
|
36
|
+
await this.loadSkillsFromDirectory(workspaceSkillsDir, 'workspace', context);
|
|
37
|
+
}
|
|
38
|
+
|
|
29
39
|
await this.loadSkillsFromDirectory(runtimeSkillsDir, 'runtime', context);
|
|
30
40
|
|
|
31
41
|
let configList: Array<{ id: string; enabled: boolean }> = [];
|
|
@@ -67,7 +77,7 @@ export class FreyaSkillRegistry {
|
|
|
67
77
|
}
|
|
68
78
|
|
|
69
79
|
/** 从指定目录加载技能到内存注册表中 */
|
|
70
|
-
private async loadSkillsFromDirectory(dirPath: string, source: 'builtin' | 'runtime', context: FreyaContext): Promise<void> {
|
|
80
|
+
private async loadSkillsFromDirectory(dirPath: string, source: 'builtin' | 'workspace' | 'runtime', context: FreyaContext): Promise<void> {
|
|
71
81
|
try {
|
|
72
82
|
const files = await fs.readdir(dirPath);
|
|
73
83
|
for (const file of files) {
|
|
@@ -77,15 +87,19 @@ export class FreyaSkillRegistry {
|
|
|
77
87
|
if (metadata.id) {
|
|
78
88
|
const defaultEnabled = metadata.defaultEnabled !== undefined
|
|
79
89
|
? metadata.defaultEnabled !== 'false'
|
|
80
|
-
: source
|
|
90
|
+
: source !== 'runtime';
|
|
91
|
+
|
|
92
|
+
const existing = this.skills.get(metadata.id);
|
|
93
|
+
const finalSource = existing?.source === 'builtin' ? 'builtin' : source;
|
|
94
|
+
const finalEnabled = existing !== undefined ? existing.enabled : defaultEnabled;
|
|
81
95
|
|
|
82
96
|
this.skills.set(metadata.id, {
|
|
83
97
|
id: metadata.id,
|
|
84
98
|
name: metadata.name || file.replace('.md', ''),
|
|
85
99
|
description: metadata.description || '',
|
|
86
100
|
content: content.trim(),
|
|
87
|
-
enabled:
|
|
88
|
-
source
|
|
101
|
+
enabled: finalEnabled,
|
|
102
|
+
source: finalSource
|
|
89
103
|
});
|
|
90
104
|
}
|
|
91
105
|
}
|
|
@@ -126,7 +140,7 @@ export class FreyaSkillRegistry {
|
|
|
126
140
|
}
|
|
127
141
|
|
|
128
142
|
skill.enabled = enabled;
|
|
129
|
-
const configSkillsPath = path.join(
|
|
143
|
+
const configSkillsPath = path.join(FREYA_HOME, 'config', 'skills.json');
|
|
130
144
|
try {
|
|
131
145
|
await this.persistSkillsConfig(configSkillsPath);
|
|
132
146
|
this.context?.logger.info(`技能 "${skill.name || skillId}" 已切换为: ${enabled ? '启用' : '禁用'}`);
|
|
@@ -5,13 +5,10 @@ import { fileURLToPath } from 'node:url';
|
|
|
5
5
|
import fs from 'node:fs';
|
|
6
6
|
|
|
7
7
|
const customHome = process.env.FREYA_HOME;
|
|
8
|
+
const customApp = process.env.FREYA_APP;
|
|
8
9
|
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
|
9
10
|
|
|
10
11
|
function resolveAppRoot(): string {
|
|
11
|
-
if (process.env.FREYA_APP_ROOT) {
|
|
12
|
-
return path.resolve(process.env.FREYA_APP_ROOT);
|
|
13
|
-
}
|
|
14
|
-
|
|
15
12
|
let current = __dirname;
|
|
16
13
|
let coreDir = current;
|
|
17
14
|
|
|
@@ -36,10 +33,17 @@ function resolveAppRoot(): string {
|
|
|
36
33
|
return coreDir;
|
|
37
34
|
}
|
|
38
35
|
|
|
39
|
-
/**
|
|
40
|
-
export const
|
|
36
|
+
/** 运行态持久化主目录(数据与配置存放区) */
|
|
37
|
+
export const FREYA_HOME = customHome
|
|
41
38
|
? path.resolve(customHome)
|
|
42
39
|
: path.join(os.homedir(), '.freya');
|
|
43
40
|
|
|
44
41
|
/** 程序代码物理安装根目录(只读代码与包内默认资源区) */
|
|
45
|
-
export const
|
|
42
|
+
export const FREYA_APP = customApp
|
|
43
|
+
? path.resolve(customApp)
|
|
44
|
+
: resolveAppRoot();
|
|
45
|
+
|
|
46
|
+
/** 宿主工程/工作区根目录(业务工程代码与定制资源区) */
|
|
47
|
+
export const FREYA_WORKSPACE = process.env.FREYA_WORKSPACE
|
|
48
|
+
? path.resolve(process.env.FREYA_WORKSPACE)
|
|
49
|
+
: process.cwd();
|
|
@@ -5,7 +5,7 @@ import fsSync from 'node:fs';
|
|
|
5
5
|
import fs from 'node:fs/promises';
|
|
6
6
|
import http from 'node:http';
|
|
7
7
|
import path from 'node:path';
|
|
8
|
-
import {
|
|
8
|
+
import { FREYA_APP } from '../utils/paths.js';
|
|
9
9
|
|
|
10
10
|
const mimeTypes: Record<string, string> = {
|
|
11
11
|
'.html': 'text/html; charset=utf-8',
|
|
@@ -43,7 +43,7 @@ export class FreyaWebContainer {
|
|
|
43
43
|
async start(ctx: FreyaContext, configManager: FreyaConfigManager): Promise<void> {
|
|
44
44
|
this.port = (ctx.config as any)?.server?.port ?? 3000;
|
|
45
45
|
this.configApi = new FreyaConfigApi(configManager);
|
|
46
|
-
const uiDist = this.getUiDistPath(
|
|
46
|
+
const uiDist = this.getUiDistPath(FREYA_APP);
|
|
47
47
|
const safePrefix = uiDist.endsWith(path.sep) ? uiDist : uiDist + path.sep;
|
|
48
48
|
|
|
49
49
|
this.httpServer = http.createServer(async (req, res) => {
|
|
@@ -113,9 +113,9 @@ export class FreyaWebContainer {
|
|
|
113
113
|
});
|
|
114
114
|
}
|
|
115
115
|
|
|
116
|
-
private getUiDistPath(
|
|
117
|
-
const devPath = path.join(
|
|
118
|
-
const prodPath = path.join(
|
|
116
|
+
private getUiDistPath(appRoot: string): string {
|
|
117
|
+
const devPath = path.join(appRoot, 'packages', 'ui', 'dist');
|
|
118
|
+
const prodPath = path.join(appRoot, 'ui');
|
|
119
119
|
if (fsSync.existsSync(devPath)) return devPath;
|
|
120
120
|
return prodPath;
|
|
121
121
|
}
|
|
@@ -16,8 +16,10 @@ export interface Logger {
|
|
|
16
16
|
export interface FreyaPaths {
|
|
17
17
|
/** 程序物理安装根目录(只读代码与程序级静态资源区) */
|
|
18
18
|
appRoot: string;
|
|
19
|
-
/**
|
|
20
|
-
|
|
19
|
+
/** 运行态持久化主目录(用户个性化配置与持久化数据区,默认 ~/.freya) */
|
|
20
|
+
homeDir: string;
|
|
21
|
+
/** 宿主工程根目录(默认为启动执行路径 process.cwd()) */
|
|
22
|
+
workspaceRoot: string;
|
|
21
23
|
/** 运行时数据持久化目录(~/.freya/data) */
|
|
22
24
|
dataDir: string;
|
|
23
25
|
/** 宿主与大模型交互隔离的文件读写沙箱(~/.freya/workspace) */
|
|
@@ -5,6 +5,7 @@ interface PluginEntry {
|
|
|
5
5
|
name: string;
|
|
6
6
|
description: string;
|
|
7
7
|
enabled: boolean;
|
|
8
|
+
source?: 'builtin' | 'workspace' | 'runtime' | 'npm';
|
|
8
9
|
}
|
|
9
10
|
|
|
10
11
|
interface PluginConfigPanelProps {
|
|
@@ -65,12 +66,27 @@ export const PluginConfigPanel: React.FC<PluginConfigPanelProps> = ({ getApiUrl
|
|
|
65
66
|
}
|
|
66
67
|
};
|
|
67
68
|
|
|
69
|
+
const getSourceTagMeta = (source?: string) => {
|
|
70
|
+
switch (source) {
|
|
71
|
+
case 'builtin':
|
|
72
|
+
return { label: '内置', bg: 'rgba(59, 130, 246, 0.15)', color: '#60a5fa' };
|
|
73
|
+
case 'workspace':
|
|
74
|
+
return { label: '集成', bg: 'rgba(168, 85, 247, 0.15)', color: '#c084fc' };
|
|
75
|
+
case 'npm':
|
|
76
|
+
return { label: 'NPM', bg: 'rgba(245, 158, 11, 0.15)', color: '#fbbf24' };
|
|
77
|
+
case 'runtime':
|
|
78
|
+
default:
|
|
79
|
+
return { label: '自定义', bg: 'rgba(16, 185, 129, 0.15)', color: '#34d399' };
|
|
80
|
+
}
|
|
81
|
+
};
|
|
82
|
+
|
|
68
83
|
return (
|
|
69
84
|
<div className="plugins-list">
|
|
70
85
|
{plugins.map((plugin) => {
|
|
71
86
|
const displayName = plugin.name || plugin.id;
|
|
72
87
|
const displayDesc = plugin.description || '未提供描述信息';
|
|
73
88
|
const shouldShowIdTag = displayName !== plugin.id;
|
|
89
|
+
const tagMeta = getSourceTagMeta(plugin.source);
|
|
74
90
|
|
|
75
91
|
return (
|
|
76
92
|
<div key={plugin.id} className="plugin-card">
|
|
@@ -82,6 +98,20 @@ export const PluginConfigPanel: React.FC<PluginConfigPanelProps> = ({ getApiUrl
|
|
|
82
98
|
({plugin.id})
|
|
83
99
|
</span>
|
|
84
100
|
)}
|
|
101
|
+
{plugin.source && (
|
|
102
|
+
<span
|
|
103
|
+
style={{
|
|
104
|
+
fontSize: '0.68rem',
|
|
105
|
+
marginLeft: '0.5rem',
|
|
106
|
+
padding: '0.1rem 0.4rem',
|
|
107
|
+
borderRadius: '4px',
|
|
108
|
+
background: tagMeta.bg,
|
|
109
|
+
color: tagMeta.color
|
|
110
|
+
}}
|
|
111
|
+
>
|
|
112
|
+
{tagMeta.label}
|
|
113
|
+
</span>
|
|
114
|
+
)}
|
|
85
115
|
</div>
|
|
86
116
|
<div className="plugin-desc">{displayDesc}</div>
|
|
87
117
|
</div>
|
|
@@ -5,7 +5,7 @@ interface SkillEntry {
|
|
|
5
5
|
name: string;
|
|
6
6
|
description: string;
|
|
7
7
|
enabled: boolean;
|
|
8
|
-
source: 'builtin' | 'runtime';
|
|
8
|
+
source: 'builtin' | 'workspace' | 'runtime';
|
|
9
9
|
}
|
|
10
10
|
|
|
11
11
|
interface SkillConfigPanelProps {
|
|
@@ -79,12 +79,24 @@ export const SkillConfigPanel: React.FC<SkillConfigPanelProps> = ({ getApiUrl })
|
|
|
79
79
|
);
|
|
80
80
|
}
|
|
81
81
|
|
|
82
|
+
const getSourceTagMeta = (source?: string) => {
|
|
83
|
+
switch (source) {
|
|
84
|
+
case 'builtin':
|
|
85
|
+
return { label: '内置', bg: 'rgba(59, 130, 246, 0.15)', color: '#60a5fa' };
|
|
86
|
+
case 'workspace':
|
|
87
|
+
return { label: '集成', bg: 'rgba(168, 85, 247, 0.15)', color: '#c084fc' };
|
|
88
|
+
case 'runtime':
|
|
89
|
+
default:
|
|
90
|
+
return { label: '自定义', bg: 'rgba(16, 185, 129, 0.15)', color: '#34d399' };
|
|
91
|
+
}
|
|
92
|
+
};
|
|
93
|
+
|
|
82
94
|
return (
|
|
83
95
|
<div className="plugins-list">
|
|
84
96
|
{skills.map((skill) => {
|
|
85
97
|
const displayName = skill.name || skill.id;
|
|
86
98
|
const displayDesc = skill.description || '未提供描述信息';
|
|
87
|
-
const
|
|
99
|
+
const tagMeta = getSourceTagMeta(skill.source);
|
|
88
100
|
|
|
89
101
|
return (
|
|
90
102
|
<div key={skill.id} className="plugin-card">
|
|
@@ -100,11 +112,11 @@ export const SkillConfigPanel: React.FC<SkillConfigPanelProps> = ({ getApiUrl })
|
|
|
100
112
|
marginLeft: '0.5rem',
|
|
101
113
|
padding: '0.1rem 0.4rem',
|
|
102
114
|
borderRadius: '4px',
|
|
103
|
-
background:
|
|
104
|
-
color:
|
|
115
|
+
background: tagMeta.bg,
|
|
116
|
+
color: tagMeta.color
|
|
105
117
|
}}
|
|
106
118
|
>
|
|
107
|
-
{
|
|
119
|
+
{tagMeta.label}
|
|
108
120
|
</span>
|
|
109
121
|
</div>
|
|
110
122
|
<div className="plugin-desc">{displayDesc}</div>
|
|
@@ -101,8 +101,8 @@ function getFormattedDateTime(): { date: string; time: string } {
|
|
|
101
101
|
|
|
102
102
|
function cleanPathFromError(err: any, ctx: FreyaContext): string {
|
|
103
103
|
const rawMessage = err?.message || String(err);
|
|
104
|
-
const
|
|
105
|
-
const escapedPath =
|
|
104
|
+
const homeDir = ctx.paths.homeDir;
|
|
105
|
+
const escapedPath = homeDir.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
|
|
106
106
|
const regex = new RegExp(escapedPath + '[\\\\/]?', 'g');
|
|
107
107
|
return rawMessage.replace(regex, '');
|
|
108
108
|
}
|
|
@@ -19,7 +19,7 @@ export class SqlAuditService {
|
|
|
19
19
|
}
|
|
20
20
|
|
|
21
21
|
const promptFileName = 'plugin.prompt.mysql.select.audit.md';
|
|
22
|
-
const runtimeOverridePath = path.join(ctx.paths.
|
|
22
|
+
const runtimeOverridePath = path.join(ctx.paths.homeDir, 'config', 'prompts', promptFileName);
|
|
23
23
|
|
|
24
24
|
try {
|
|
25
25
|
const content = await fs.readFile(runtimeOverridePath, 'utf-8');
|