@eoasmxd/freya 0.4.4 → 0.5.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.
- 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.js +4 -4
- 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 +4 -4
- 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
|
@@ -80,8 +80,8 @@ function getFormattedDateTime() {
|
|
|
80
80
|
}
|
|
81
81
|
function cleanPathFromError(err, ctx) {
|
|
82
82
|
const rawMessage = err?.message || String(err);
|
|
83
|
-
const
|
|
84
|
-
const escapedPath =
|
|
83
|
+
const homeDir = ctx.paths.homeDir;
|
|
84
|
+
const escapedPath = homeDir.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
|
|
85
85
|
const regex = new RegExp(escapedPath + '[\\\\/]?', 'g');
|
|
86
86
|
return rawMessage.replace(regex, '');
|
|
87
87
|
}
|
|
@@ -10,7 +10,7 @@ export class SqlAuditService {
|
|
|
10
10
|
return this.cachedPrompt;
|
|
11
11
|
}
|
|
12
12
|
const promptFileName = 'plugin.prompt.mysql.select.audit.md';
|
|
13
|
-
const runtimeOverridePath = path.join(ctx.paths.
|
|
13
|
+
const runtimeOverridePath = path.join(ctx.paths.homeDir, 'config', 'prompts', promptFileName);
|
|
14
14
|
try {
|
|
15
15
|
const content = await fs.readFile(runtimeOverridePath, 'utf-8');
|
|
16
16
|
if (content.trim()) {
|
|
@@ -6,7 +6,7 @@ import { FreyaConfigFileHandler } from './file-handler.js';
|
|
|
6
6
|
import { FreyaConfigSchemaRegistry } from './schema-registry.js';
|
|
7
7
|
import type { FreyaSkillRegistry, FreyaSkill } from '../skill/skill-registry.js';
|
|
8
8
|
import path from 'node:path';
|
|
9
|
-
import {
|
|
9
|
+
import { FREYA_HOME } from '../utils/paths.js';
|
|
10
10
|
|
|
11
11
|
function cleanPathFromError(err: any): string {
|
|
12
12
|
const rawMessage = err?.message || String(err);
|
|
@@ -226,10 +226,10 @@ export class FreyaConfigManager {
|
|
|
226
226
|
const cloned = JSON.parse(JSON.stringify(config));
|
|
227
227
|
if (cloned.workspace && typeof cloned.workspace === 'string') {
|
|
228
228
|
if (!path.isAbsolute(cloned.workspace)) {
|
|
229
|
-
cloned.workspace = path.resolve(
|
|
229
|
+
cloned.workspace = path.resolve(FREYA_HOME, cloned.workspace);
|
|
230
230
|
}
|
|
231
231
|
} else {
|
|
232
|
-
cloned.workspace = path.join(
|
|
232
|
+
cloned.workspace = path.join(FREYA_HOME, 'workspace');
|
|
233
233
|
}
|
|
234
234
|
(this.context as any).config = deepFreeze(cloned);
|
|
235
235
|
}
|
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
import fs from 'node:fs/promises';
|
|
2
2
|
import path from 'node:path';
|
|
3
|
-
import {
|
|
3
|
+
import { FREYA_HOME } from '../utils/paths.js';
|
|
4
4
|
|
|
5
5
|
/** 配置文件底层 IO 处理器 */
|
|
6
6
|
export class FreyaConfigFileHandler {
|
|
7
7
|
async readFreyaConfig(): Promise<Record<string, any>> {
|
|
8
|
-
const filePath = path.join(
|
|
8
|
+
const filePath = path.join(FREYA_HOME, 'config', 'freya.json');
|
|
9
9
|
try {
|
|
10
10
|
const data = await fs.readFile(filePath, 'utf-8');
|
|
11
11
|
return JSON.parse(data);
|
|
@@ -15,13 +15,13 @@ export class FreyaConfigFileHandler {
|
|
|
15
15
|
}
|
|
16
16
|
|
|
17
17
|
async writeFreyaConfig(config: Record<string, any>): Promise<void> {
|
|
18
|
-
const filePath = path.join(
|
|
18
|
+
const filePath = path.join(FREYA_HOME, 'config', 'freya.json');
|
|
19
19
|
await fs.mkdir(path.dirname(filePath), { recursive: true });
|
|
20
20
|
await fs.writeFile(filePath, JSON.stringify(config, null, 2) + '\n', 'utf-8');
|
|
21
21
|
}
|
|
22
22
|
|
|
23
23
|
async readProviders(): Promise<any[]> {
|
|
24
|
-
const filePath = path.join(
|
|
24
|
+
const filePath = path.join(FREYA_HOME, 'config', 'providers.json');
|
|
25
25
|
try {
|
|
26
26
|
const data = await fs.readFile(filePath, 'utf-8');
|
|
27
27
|
return JSON.parse(data);
|
|
@@ -31,18 +31,18 @@ export class FreyaConfigFileHandler {
|
|
|
31
31
|
}
|
|
32
32
|
|
|
33
33
|
async writeProviders(providers: any[]): Promise<void> {
|
|
34
|
-
const filePath = path.join(
|
|
34
|
+
const filePath = path.join(FREYA_HOME, 'config', 'providers.json');
|
|
35
35
|
await fs.mkdir(path.dirname(filePath), { recursive: true });
|
|
36
36
|
await fs.writeFile(filePath, JSON.stringify(providers, null, 2) + '\n', 'utf-8');
|
|
37
37
|
}
|
|
38
38
|
|
|
39
39
|
async readPromptFile(name: string): Promise<string> {
|
|
40
|
-
const filePath = path.join(
|
|
40
|
+
const filePath = path.join(FREYA_HOME, 'config', `${name.toUpperCase()}.md`);
|
|
41
41
|
return await fs.readFile(filePath, 'utf-8');
|
|
42
42
|
}
|
|
43
43
|
|
|
44
44
|
async writePromptFile(name: string, content: string): Promise<void> {
|
|
45
|
-
const filePath = path.join(
|
|
45
|
+
const filePath = path.join(FREYA_HOME, 'config', `${name.toUpperCase()}.md`);
|
|
46
46
|
await fs.mkdir(path.dirname(filePath), { recursive: true });
|
|
47
47
|
await fs.writeFile(filePath, content.trim() + '\n', 'utf-8');
|
|
48
48
|
}
|
|
@@ -6,7 +6,7 @@ import type {
|
|
|
6
6
|
Logger,
|
|
7
7
|
} from '@eoasmxd/freya-sdk';
|
|
8
8
|
import path from 'node:path';
|
|
9
|
-
import {
|
|
9
|
+
import { FREYA_APP, FREYA_HOME, FREYA_WORKSPACE } from './utils/paths.js';
|
|
10
10
|
|
|
11
11
|
export class DefaultFreyaContext implements FreyaContext {
|
|
12
12
|
logger!: Logger;
|
|
@@ -15,17 +15,18 @@ export class DefaultFreyaContext implements FreyaContext {
|
|
|
15
15
|
llm!: ILLMService;
|
|
16
16
|
get paths(): FreyaPaths {
|
|
17
17
|
const configWorkspace = this.config?.workspace;
|
|
18
|
-
let workspaceDir = path.join(
|
|
18
|
+
let workspaceDir = path.join(FREYA_HOME, 'workspace');
|
|
19
19
|
if (configWorkspace && typeof configWorkspace === 'string') {
|
|
20
20
|
workspaceDir = path.isAbsolute(configWorkspace)
|
|
21
21
|
? configWorkspace
|
|
22
|
-
: path.resolve(
|
|
22
|
+
: path.resolve(FREYA_HOME, configWorkspace);
|
|
23
23
|
}
|
|
24
24
|
|
|
25
25
|
return {
|
|
26
|
-
appRoot:
|
|
27
|
-
|
|
28
|
-
|
|
26
|
+
appRoot: FREYA_APP,
|
|
27
|
+
homeDir: FREYA_HOME,
|
|
28
|
+
workspaceRoot: FREYA_WORKSPACE,
|
|
29
|
+
dataDir: path.join(FREYA_HOME, 'data'),
|
|
29
30
|
workspaceDir,
|
|
30
31
|
};
|
|
31
32
|
}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import type { LLMMessage, LLMTokenUsage, ToolDefinition } from '@eoasmxd/freya-sdk';
|
|
2
2
|
import fs from 'node:fs';
|
|
3
3
|
import path from 'node:path';
|
|
4
|
-
import {
|
|
4
|
+
import { FREYA_HOME } from '../utils/paths.js';
|
|
5
5
|
|
|
6
6
|
/** LLM 交互日志器,按日期写入 logs/llm-YYYY-MM-DD.log */
|
|
7
7
|
export class FreyaLLMLogger {
|
|
@@ -9,7 +9,7 @@ export class FreyaLLMLogger {
|
|
|
9
9
|
private _enabled: boolean;
|
|
10
10
|
|
|
11
11
|
constructor(enabled: boolean) {
|
|
12
|
-
this.logsDir = path.join(
|
|
12
|
+
this.logsDir = path.join(FREYA_HOME, 'logs');
|
|
13
13
|
this._enabled = enabled;
|
|
14
14
|
fs.mkdirSync(this.logsDir, { recursive: true });
|
|
15
15
|
}
|
|
@@ -3,7 +3,7 @@ import fs from 'node:fs';
|
|
|
3
3
|
import fsPromises from 'node:fs/promises';
|
|
4
4
|
import path from 'node:path';
|
|
5
5
|
import { inspect } from 'node:util';
|
|
6
|
-
import {
|
|
6
|
+
import { FREYA_HOME } from './utils/paths.js';
|
|
7
7
|
|
|
8
8
|
/** 双轨日志器:按日期滚动写入文件,按配置输出至控制台 */
|
|
9
9
|
export class FreyaLogger implements Logger {
|
|
@@ -19,7 +19,7 @@ export class FreyaLogger implements Logger {
|
|
|
19
19
|
private currentLogFilePath = '';
|
|
20
20
|
|
|
21
21
|
constructor() {
|
|
22
|
-
this.logsDir = path.join(
|
|
22
|
+
this.logsDir = path.join(FREYA_HOME, 'logs');
|
|
23
23
|
fs.mkdirSync(this.logsDir, { recursive: true });
|
|
24
24
|
}
|
|
25
25
|
|
|
@@ -6,7 +6,7 @@ import { pathToFileURL } from 'node:url';
|
|
|
6
6
|
import { FreyaCommandRegistry } from '../command/command-registry.js';
|
|
7
7
|
import { FreyaConfigSchemaRegistry } from '../config/schema-registry.js';
|
|
8
8
|
import { FreyaPromptRegistry } from '../prompt/prompt-registry.js';
|
|
9
|
-
import {
|
|
9
|
+
import { FREYA_APP, FREYA_HOME, FREYA_WORKSPACE } from '../utils/paths.js';
|
|
10
10
|
import { FreyaPluginRegistry } from './plugin-registry.js';
|
|
11
11
|
|
|
12
12
|
export interface PluginConfigEntry {
|
|
@@ -18,7 +18,7 @@ export interface PluginConfigEntry {
|
|
|
18
18
|
displayName?: string;
|
|
19
19
|
description?: string;
|
|
20
20
|
version?: string;
|
|
21
|
-
source?: 'builtin' | 'runtime' | 'npm';
|
|
21
|
+
source?: 'builtin' | 'workspace' | 'runtime' | 'npm';
|
|
22
22
|
}
|
|
23
23
|
|
|
24
24
|
interface DiscoveredPluginInfo {
|
|
@@ -28,7 +28,7 @@ interface DiscoveredPluginInfo {
|
|
|
28
28
|
displayName: string;
|
|
29
29
|
description: string;
|
|
30
30
|
version: string;
|
|
31
|
-
source: 'builtin' | 'runtime' | 'npm';
|
|
31
|
+
source: 'builtin' | 'workspace' | 'runtime' | 'npm';
|
|
32
32
|
defaultEnabled?: boolean;
|
|
33
33
|
prompts?: string[];
|
|
34
34
|
valid: boolean;
|
|
@@ -164,7 +164,7 @@ export class FreyaPluginManager {
|
|
|
164
164
|
entry.enabled = enabled;
|
|
165
165
|
entry.status = enabled ? 'active' : 'disabled';
|
|
166
166
|
|
|
167
|
-
const configPluginsPath = path.join(
|
|
167
|
+
const configPluginsPath = path.join(FREYA_HOME, 'config', 'plugins.json');
|
|
168
168
|
try {
|
|
169
169
|
await fs.mkdir(path.dirname(configPluginsPath), { recursive: true });
|
|
170
170
|
const rawEntries = this.pluginEntries.map((e) => ({ id: e.id, enabled: e.enabled }));
|
|
@@ -232,7 +232,7 @@ export class FreyaPluginManager {
|
|
|
232
232
|
*/
|
|
233
233
|
private async inspectPluginPackage(
|
|
234
234
|
dirPath: string,
|
|
235
|
-
source: 'builtin' | 'runtime' | 'npm'
|
|
235
|
+
source: 'builtin' | 'workspace' | 'runtime' | 'npm'
|
|
236
236
|
): Promise<DiscoveredPluginInfo | null> {
|
|
237
237
|
try {
|
|
238
238
|
const pkgPath = path.join(dirPath, 'package.json');
|
|
@@ -247,7 +247,7 @@ export class FreyaPluginManager {
|
|
|
247
247
|
const displayName = String(pkg.freya?.displayName || pkg.displayName || id);
|
|
248
248
|
const description = String(pkg.description || '');
|
|
249
249
|
const version = String(pkg.version || '0.1.0');
|
|
250
|
-
const defaultEnabled = (source === 'builtin' && pkg.freya?.defaultEnabled === true);
|
|
250
|
+
const defaultEnabled = ((source === 'builtin' || source === 'workspace') && pkg.freya?.defaultEnabled === true);
|
|
251
251
|
const rawPrompts = pkg.freya?.prompts;
|
|
252
252
|
const prompts = Array.isArray(rawPrompts) ? rawPrompts.map(String) : [];
|
|
253
253
|
|
|
@@ -354,7 +354,7 @@ export class FreyaPluginManager {
|
|
|
354
354
|
let pkgJsonPath = '';
|
|
355
355
|
try {
|
|
356
356
|
pkgJsonPath = req.resolve(`${pkgName}/package.json`, {
|
|
357
|
-
paths: [path.join(
|
|
357
|
+
paths: [path.join(FREYA_WORKSPACE), path.join(FREYA_HOME), path.join(FREYA_APP), process.cwd()]
|
|
358
358
|
});
|
|
359
359
|
} catch {
|
|
360
360
|
return {
|
|
@@ -407,7 +407,7 @@ export class FreyaPluginManager {
|
|
|
407
407
|
private async scanAllChannels(configuredIds: Set<string>): Promise<DiscoveredPluginInfo[]> {
|
|
408
408
|
const map = new Map<string, DiscoveredPluginInfo>();
|
|
409
409
|
|
|
410
|
-
const builtinDir = path.join(
|
|
410
|
+
const builtinDir = path.join(FREYA_APP, 'plugins');
|
|
411
411
|
try {
|
|
412
412
|
const entries = await fs.readdir(builtinDir, { withFileTypes: true });
|
|
413
413
|
for (const entry of entries) {
|
|
@@ -418,13 +418,42 @@ export class FreyaPluginManager {
|
|
|
418
418
|
}
|
|
419
419
|
} catch { }
|
|
420
420
|
|
|
421
|
-
const
|
|
421
|
+
const workspaceDir = path.join(FREYA_WORKSPACE, 'plugins');
|
|
422
|
+
const resolvedBuiltin = path.resolve(builtinDir);
|
|
423
|
+
const resolvedWorkspace = path.resolve(workspaceDir);
|
|
424
|
+
const resolvedRuntime = path.resolve(path.join(FREYA_HOME, 'plugins'));
|
|
425
|
+
|
|
426
|
+
if (resolvedWorkspace !== resolvedBuiltin && resolvedWorkspace !== resolvedRuntime) {
|
|
427
|
+
try {
|
|
428
|
+
const entries = await fs.readdir(workspaceDir, { withFileTypes: true });
|
|
429
|
+
for (const entry of entries) {
|
|
430
|
+
if (entry.isDirectory()) {
|
|
431
|
+
const info = await this.inspectPluginPackage(path.join(workspaceDir, entry.name), 'workspace');
|
|
432
|
+
if (info) {
|
|
433
|
+
const existing = map.get(info.id);
|
|
434
|
+
if (existing?.source === 'builtin') {
|
|
435
|
+
info.source = 'builtin';
|
|
436
|
+
}
|
|
437
|
+
map.set(info.id, info);
|
|
438
|
+
}
|
|
439
|
+
}
|
|
440
|
+
}
|
|
441
|
+
} catch { }
|
|
442
|
+
}
|
|
443
|
+
|
|
444
|
+
const runtimeDir = path.join(FREYA_HOME, 'plugins');
|
|
422
445
|
try {
|
|
423
446
|
const entries = await fs.readdir(runtimeDir, { withFileTypes: true });
|
|
424
447
|
for (const entry of entries) {
|
|
425
448
|
if (entry.isDirectory()) {
|
|
426
449
|
const info = await this.inspectPluginPackage(path.join(runtimeDir, entry.name), 'runtime');
|
|
427
|
-
if (info)
|
|
450
|
+
if (info) {
|
|
451
|
+
const existing = map.get(info.id);
|
|
452
|
+
if (existing?.source === 'builtin') {
|
|
453
|
+
info.source = 'builtin';
|
|
454
|
+
}
|
|
455
|
+
map.set(info.id, info);
|
|
456
|
+
}
|
|
428
457
|
}
|
|
429
458
|
}
|
|
430
459
|
} catch { }
|
|
@@ -445,7 +474,7 @@ export class FreyaPluginManager {
|
|
|
445
474
|
async loadConfiguredPlugins(pluginRegistry: FreyaPluginRegistry, ctx: FreyaContext): Promise<void> {
|
|
446
475
|
this.ctx = ctx;
|
|
447
476
|
this.pluginRegistry = pluginRegistry;
|
|
448
|
-
const configPluginsPath = path.join(
|
|
477
|
+
const configPluginsPath = path.join(FREYA_HOME, 'config', 'plugins.json');
|
|
449
478
|
|
|
450
479
|
let configList: Array<{ id: string; enabled: boolean }> = [];
|
|
451
480
|
try {
|
|
@@ -2,14 +2,14 @@ import type { Logger } from '@eoasmxd/freya-sdk';
|
|
|
2
2
|
import fs from 'node:fs/promises';
|
|
3
3
|
import path from 'node:path';
|
|
4
4
|
import { fileURLToPath } from 'node:url';
|
|
5
|
-
import {
|
|
5
|
+
import { FREYA_APP, FREYA_HOME } from '../utils/paths.js';
|
|
6
6
|
import { FreyaPromptRegistry } from './prompt-registry.js';
|
|
7
7
|
|
|
8
8
|
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
|
9
9
|
|
|
10
10
|
/** 提示词物理文件管理器,负责加载、缺失拷贝与持久化覆写 */
|
|
11
11
|
export class FreyaPromptManager {
|
|
12
|
-
private defaultDirPath = path.join(
|
|
12
|
+
private defaultDirPath = path.join(FREYA_APP, 'config', 'prompts');
|
|
13
13
|
|
|
14
14
|
constructor(
|
|
15
15
|
private promptRegistry: FreyaPromptRegistry,
|
|
@@ -31,7 +31,7 @@ export class FreyaPromptManager {
|
|
|
31
31
|
throw new Error(`拒绝执行:配置管理工具仅允许管理核心提示词`);
|
|
32
32
|
}
|
|
33
33
|
|
|
34
|
-
const runFilePath = path.join(
|
|
34
|
+
const runFilePath = path.join(FREYA_HOME, 'config', `${name.toUpperCase()}.md`);
|
|
35
35
|
await fs.mkdir(path.dirname(runFilePath), { recursive: true });
|
|
36
36
|
await fs.writeFile(runFilePath, content, 'utf-8');
|
|
37
37
|
|
|
@@ -46,7 +46,7 @@ export class FreyaPromptManager {
|
|
|
46
46
|
throw new Error(`拒绝执行:配置管理工具仅允许管理核心提示词`);
|
|
47
47
|
}
|
|
48
48
|
|
|
49
|
-
const runFilePath = path.join(
|
|
49
|
+
const runFilePath = path.join(FREYA_HOME, 'config', `${name.toUpperCase()}.md`);
|
|
50
50
|
const registryKey = `core.prompt.${name.toLowerCase()}`;
|
|
51
51
|
const currentText = this.promptRegistry.get(registryKey);
|
|
52
52
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import fs from 'node:fs/promises';
|
|
2
2
|
import path from 'node:path';
|
|
3
|
-
import {
|
|
3
|
+
import { FREYA_APP, FREYA_HOME } from '../utils/paths.js';
|
|
4
4
|
|
|
5
5
|
export interface FreyaPrompt {
|
|
6
6
|
key: string;
|
|
@@ -14,7 +14,7 @@ export class FreyaPromptRegistry {
|
|
|
14
14
|
private prompts = new Map<string, FreyaPrompt>();
|
|
15
15
|
|
|
16
16
|
private getRunFilePath(prompt: Omit<FreyaPrompt, 'content'>): string {
|
|
17
|
-
return prompt.runPath || path.join(
|
|
17
|
+
return prompt.runPath || path.join(FREYA_HOME, 'config', 'prompts', path.basename(prompt.defaultPath));
|
|
18
18
|
}
|
|
19
19
|
|
|
20
20
|
/** 注册提示词元数据声明并执行异步双读载入 */
|
|
@@ -60,14 +60,14 @@ export class FreyaPromptRegistry {
|
|
|
60
60
|
|
|
61
61
|
/** 扫描并装载所有内核提示词,支持运行时提示词覆盖默认提示词 */
|
|
62
62
|
async loadKernelPrompts(): Promise<void> {
|
|
63
|
-
const defaultDirPath = path.join(
|
|
63
|
+
const defaultDirPath = path.join(FREYA_APP, 'config', 'prompts');
|
|
64
64
|
try {
|
|
65
65
|
const corePrompts = ['identity', 'soul', 'tools', 'agents', 'user', 'memory'];
|
|
66
66
|
for (const name of corePrompts) {
|
|
67
67
|
await this.register({
|
|
68
68
|
key: `core.prompt.${name}`,
|
|
69
69
|
defaultPath: path.join(defaultDirPath, `core.prompt.${name}.md`),
|
|
70
|
-
runPath: path.join(
|
|
70
|
+
runPath: path.join(FREYA_HOME, 'config', `${name.toUpperCase()}.md`)
|
|
71
71
|
});
|
|
72
72
|
}
|
|
73
73
|
|
|
@@ -1,10 +1,10 @@
|
|
|
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_HOME } from '../utils/paths.js';
|
|
5
5
|
import type { Session, SessionData, SessionIndex, SnapFile } from './types.js';
|
|
6
6
|
|
|
7
|
-
const DATA_DIR = path.resolve(
|
|
7
|
+
const DATA_DIR = path.resolve(FREYA_HOME, 'data');
|
|
8
8
|
const SESSIONS_DIR = path.resolve(DATA_DIR, 'sessions');
|
|
9
9
|
const INDEX_FILE = path.resolve(DATA_DIR, 'sessions.json');
|
|
10
10
|
|
|
@@ -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) */
|