pulse-coder-engine 0.0.1-alpha.1 → 0.0.1-alpha.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.
@@ -1,426 +0,0 @@
1
- import { promises as fs } from 'fs';
2
- import path from 'path';
3
- import { glob } from 'glob';
4
- import { EventEmitter } from 'events';
5
-
6
- import type { EnginePlugin, EnginePluginContext, EnginePluginLoadOptions } from './EnginePlugin.js';
7
- import type { UserConfigPlugin, UserConfigPluginLoadOptions } from './UserConfigPlugin.js';
8
- import { ConfigVariableResolver } from './UserConfigPlugin.js';
9
-
10
- /**
11
- * 插件管理器 - 管理双轨插件系统
12
- */
13
- export class PluginManager {
14
- private enginePlugins = new Map<string, EnginePlugin>();
15
- private userConfigPlugins: UserConfigPlugin[] = [];
16
- private tools = new Map<string, any>();
17
- private services = new Map<string, any>();
18
- private protocols = new Map<string, any>();
19
- private config = new Map<string, any>();
20
-
21
- private events = new EventEmitter();
22
- private logger = {
23
- debug: (msg: string, meta?: any) => console.debug(`[PluginManager] ${msg}`, meta),
24
- info: (msg: string, meta?: any) => console.info(`[PluginManager] ${msg}`, meta),
25
- warn: (msg: string, meta?: any) => console.warn(`[PluginManager] ${msg}`, meta),
26
- error: (msg: string, error?: Error, meta?: any) => console.error(`[PluginManager] ${msg}`, error, meta)
27
- };
28
-
29
- /**
30
- * 初始化插件系统
31
- */
32
- async initialize(options: {
33
- enginePlugins?: EnginePluginLoadOptions;
34
- userConfigPlugins?: UserConfigPluginLoadOptions;
35
- } = {}): Promise<void> {
36
- this.logger.info('Initializing plugin system...');
37
-
38
- try {
39
- // 1. 加载引擎插件(优先)
40
- await this.loadEnginePlugins(options.enginePlugins);
41
-
42
- // 2. 验证核心能力
43
- await this.validateCoreCapabilities();
44
-
45
- // 3. 加载用户配置插件
46
- await this.loadUserConfigPlugins(options.userConfigPlugins);
47
-
48
- this.logger.info('Plugin system initialized successfully');
49
- } catch (error) {
50
- this.logger.error('Failed to initialize plugin system', error as Error);
51
- throw error;
52
- }
53
- }
54
-
55
- /**
56
- * 加载引擎插件
57
- */
58
- private async loadEnginePlugins(options: EnginePluginLoadOptions = {}): Promise<void> {
59
- const plugins: EnginePlugin[] = [];
60
-
61
- // 1. API传入的插件(最高优先级)
62
- if (options.plugins) {
63
- plugins.push(...options.plugins);
64
- }
65
-
66
- // 2. 目录扫描的插件
67
- if (options.scan !== false) {
68
- const scanPaths = options.dirs || [
69
- '.pulse-coder/engine-plugins',
70
- '.coder/engine-plugins',
71
- '~/.pulse-coder/engine-plugins',
72
- '~/.coder/engine-plugins'
73
- ];
74
-
75
- for (const dir of scanPaths) {
76
- const scannedPlugins = await this.scanEnginePlugins(dir);
77
- plugins.push(...scannedPlugins);
78
- }
79
- }
80
-
81
- // 3. 按依赖顺序初始化插件
82
- const sortedPlugins = this.sortPluginsByDependencies(plugins);
83
-
84
- for (const plugin of sortedPlugins) {
85
- await this.initializeEnginePlugin(plugin);
86
- }
87
- }
88
-
89
- /**
90
- * 扫描引擎插件目录
91
- */
92
- private async scanEnginePlugins(dir: string): Promise<EnginePlugin[]> {
93
- const plugins: EnginePlugin[] = [];
94
- const resolvedDir = this.resolvePath(dir);
95
-
96
- try {
97
- const pattern = '**/*.plugin.{js,ts}';
98
- const files = await glob(pattern, { cwd: resolvedDir, absolute: true });
99
-
100
- for (const file of files) {
101
- try {
102
- const plugin = await this.loadEnginePluginFile(file);
103
- plugins.push(plugin);
104
- this.logger.info(`Loaded engine plugin: ${plugin.name} from ${file}`);
105
- } catch (error) {
106
- this.logger.error(`Failed to load engine plugin from ${file}`, error as Error);
107
- }
108
- }
109
- } catch (error) {
110
- // 目录不存在是正常现象
111
- this.logger.debug(`Directory not found: ${resolvedDir}`);
112
- }
113
-
114
- return plugins;
115
- }
116
-
117
- /**
118
- * 加载单个引擎插件文件
119
- */
120
- private async loadEnginePluginFile(filePath: string): Promise<EnginePlugin> {
121
- const plugin = await import(filePath);
122
-
123
- // 支持 default export 或直接导出
124
- const enginePlugin = plugin.default || plugin;
125
-
126
- if (!enginePlugin.name || !enginePlugin.initialize) {
127
- throw new Error(`Invalid engine plugin: ${filePath}`);
128
- }
129
-
130
- return enginePlugin;
131
- }
132
-
133
- /**
134
- * 初始化单个引擎插件
135
- */
136
- private async initializeEnginePlugin(plugin: EnginePlugin): Promise<void> {
137
- try {
138
- // 检查依赖
139
- if (plugin.dependencies) {
140
- for (const dep of plugin.dependencies) {
141
- if (!this.enginePlugins.has(dep)) {
142
- throw new Error(`Dependency not found: ${dep} for plugin ${plugin.name}`);
143
- }
144
- }
145
- }
146
-
147
- const context: EnginePluginContext = {
148
- registerTool: (name, tool) => {
149
- this.tools.set(name, tool);
150
- },
151
- registerTools: (tools) => {
152
- Object.entries(tools).forEach(([name, tool]) => {
153
- this.tools.set(name, tool);
154
- });
155
- },
156
- getTool: (name) => this.tools.get(name),
157
-
158
- registerProtocol: (name, handler) => {
159
- this.protocols.set(name, handler);
160
- },
161
- getProtocol: (name) => this.protocols.get(name),
162
-
163
- registerService: (name, service) => {
164
- this.services.set(name, service);
165
- },
166
- getService: (name) => this.services.get(name),
167
-
168
- getConfig: (key) => this.config.get(key),
169
- setConfig: (key, value) => this.config.set(key, value),
170
-
171
- events: this.events,
172
- logger: this.logger
173
- };
174
-
175
- // 执行生命周期钩子
176
- if (plugin.beforeInitialize) {
177
- await plugin.beforeInitialize(context);
178
- }
179
-
180
- await plugin.initialize(context);
181
-
182
- if (plugin.afterInitialize) {
183
- await plugin.afterInitialize(context);
184
- }
185
-
186
- this.enginePlugins.set(plugin.name, plugin);
187
-
188
- } catch (error) {
189
- throw new Error(`Failed to initialize engine plugin ${plugin.name}: ${error}`);
190
- }
191
- }
192
-
193
- /**
194
- * 加载用户配置插件
195
- */
196
- private async loadUserConfigPlugins(options: UserConfigPluginLoadOptions = {}): Promise<void> {
197
- const configs: UserConfigPlugin[] = [];
198
-
199
- // 1. API传入的配置
200
- if (options.configs) {
201
- configs.push(...options.configs);
202
- }
203
-
204
- // 2. 目录扫描的配置
205
- if (options.scan !== false) {
206
- const scanPaths = options.dirs || [
207
- '.pulse-coder/config',
208
- '.coder/config',
209
- '~/.pulse-coder/config',
210
- '~/.coder/config'
211
- ];
212
-
213
- for (const dir of scanPaths) {
214
- const scannedConfigs = await this.scanUserConfigPlugins(dir);
215
- configs.push(...scannedConfigs);
216
- }
217
- }
218
-
219
- // 3. 应用所有配置
220
- for (const config of configs) {
221
- await this.applyUserConfig(config);
222
- }
223
- }
224
-
225
- /**
226
- * 扫描用户配置插件目录
227
- */
228
- private async scanUserConfigPlugins(dir: string): Promise<UserConfigPlugin[]> {
229
- const configs: UserConfigPlugin[] = [];
230
- const resolvedDir = this.resolvePath(dir);
231
-
232
- try {
233
- const patterns = ['config.{json,yaml,yml}', '*.config.{json,yaml,yml}'];
234
-
235
- for (const pattern of patterns) {
236
- const files = await glob(pattern, { cwd: resolvedDir, absolute: true });
237
-
238
- for (const file of files) {
239
- try {
240
- const config = await this.loadUserConfigFile(file);
241
- configs.push(config);
242
- this.logger.info(`Loaded user config: ${config.name || file}`);
243
- } catch (error) {
244
- this.logger.error(`Failed to load user config from ${file}`, error as Error);
245
- }
246
- }
247
- }
248
- } catch (error) {
249
- this.logger.debug(`Directory not found: ${resolvedDir}`);
250
- }
251
-
252
- return configs;
253
- }
254
-
255
- /**
256
- * 加载单个用户配置文件
257
- */
258
- private async loadUserConfigFile(filePath: string): Promise<UserConfigPlugin> {
259
- const content = await fs.readFile(filePath, 'utf-8');
260
- const ext = path.extname(filePath);
261
-
262
- let config: UserConfigPlugin;
263
-
264
- try {
265
- if (ext === '.json') {
266
- config = JSON.parse(content);
267
- } else if (ext === '.yaml' || ext === '.yml') {
268
- // 使用动态导入避免依赖问题
269
- const YAML = await import('yaml');
270
- config = YAML.parse(content);
271
- } else {
272
- throw new Error(`Unsupported config format: ${ext}`);
273
- }
274
- } catch (error) {
275
- throw new Error(`Failed to parse config file ${filePath}: ${error}`);
276
- }
277
-
278
- // 环境变量替换
279
- const resolver = new ConfigVariableResolver();
280
- config = resolver.resolveObject(config);
281
-
282
- return config;
283
- }
284
-
285
- /**
286
- * 应用用户配置
287
- */
288
- private async applyUserConfig(config: UserConfigPlugin): Promise<void> {
289
- try {
290
- // 配置工具
291
- if (config.tools) {
292
- for (const [name, toolConfig] of Object.entries(config.tools)) {
293
- if (toolConfig.enabled !== false) {
294
- // 这里将根据配置创建具体工具实例
295
- this.logger.debug(`Configured tool: ${name}`);
296
- }
297
- }
298
- }
299
-
300
- // 配置MCP服务器
301
- if (config.mcp?.servers) {
302
- for (const server of config.mcp.servers) {
303
- if (server.enabled !== false) {
304
- this.logger.debug(`Configured MCP server: ${server.name}`);
305
- }
306
- }
307
- }
308
-
309
- // 配置子代理
310
- if (config.subAgents) {
311
- for (const agent of config.subAgents) {
312
- if (agent.enabled !== false) {
313
- this.logger.debug(`Configured sub-agent: ${agent.name}`);
314
- }
315
- }
316
- }
317
-
318
- // 配置技能(向后兼容)
319
- if (config.skills) {
320
- this.logger.debug(`Configured skills scanning`, config.skills);
321
- }
322
-
323
- this.userConfigPlugins.push(config);
324
-
325
- } catch (error) {
326
- this.logger.error('Failed to apply user config', error as Error);
327
- }
328
- }
329
-
330
- /**
331
- * 验证核心能力
332
- */
333
- private async validateCoreCapabilities(): Promise<void> {
334
- // 检查必需的核心插件是否已加载
335
- const requiredCapabilities = [
336
- 'skill-registry' // 确保skill系统可用
337
- ];
338
-
339
- for (const capability of requiredCapabilities) {
340
- if (!this.enginePlugins.has(capability) && !this.enginePlugins.has(`@pulse-coder/engine-${capability}`)) {
341
- this.logger.warn(`Missing core capability: ${capability}`);
342
- }
343
- }
344
- }
345
-
346
- /**
347
- * 插件依赖排序
348
- */
349
- private sortPluginsByDependencies(plugins: EnginePlugin[]): EnginePlugin[] {
350
- const sorted: EnginePlugin[] = [];
351
- const visited = new Set<string>();
352
- const visiting = new Set<string>();
353
-
354
- const visit = (plugin: EnginePlugin) => {
355
- if (visited.has(plugin.name)) return;
356
- if (visiting.has(plugin.name)) {
357
- throw new Error(`Circular dependency detected: ${plugin.name}`);
358
- }
359
-
360
- visiting.add(plugin.name);
361
-
362
- if (plugin.dependencies) {
363
- for (const dep of plugin.dependencies) {
364
- const depPlugin = plugins.find(p => p.name === dep);
365
- if (depPlugin) {
366
- visit(depPlugin);
367
- }
368
- }
369
- }
370
-
371
- visiting.delete(plugin.name);
372
- visited.add(plugin.name);
373
- sorted.push(plugin);
374
- };
375
-
376
- for (const plugin of plugins) {
377
- visit(plugin);
378
- }
379
-
380
- return sorted;
381
- }
382
-
383
- /**
384
- * 工具获取
385
- */
386
- getTools(): Record<string, any> {
387
- return Object.fromEntries(this.tools);
388
- }
389
-
390
- /**
391
- * 服务获取
392
- */
393
- getService<T>(name: string): T | undefined {
394
- return this.services.get(name);
395
- }
396
-
397
- /**
398
- * 协议获取
399
- */
400
- getProtocol(name: string): any {
401
- return this.protocols.get(name);
402
- }
403
-
404
- /**
405
- * 获取插件状态
406
- */
407
- getStatus() {
408
- return {
409
- enginePlugins: Array.from(this.enginePlugins.keys()),
410
- userConfigPlugins: this.userConfigPlugins.map(c => c.name || 'unnamed'),
411
- tools: Array.from(this.tools.keys()),
412
- services: Array.from(this.services.keys()),
413
- protocols: Array.from(this.protocols.keys())
414
- };
415
- }
416
-
417
- /**
418
- * 解析路径(支持 ~ 和相对路径)
419
- */
420
- private resolvePath(dir: string): string {
421
- if (dir.startsWith('~/')) {
422
- return path.join(process.env.HOME || process.env.USERPROFILE || '', dir.slice(2));
423
- }
424
- return path.resolve(dir);
425
- }
426
- }
@@ -1,183 +0,0 @@
1
- import { z } from 'zod';
2
-
3
- /**
4
- * 用户配置插件接口 - 面向终端用户
5
- * 使用声明式配置扩展引擎能力
6
- */
7
- export interface UserConfigPlugin {
8
- version: string;
9
- name?: string;
10
- description?: string;
11
-
12
- // 工具配置 - 使用现有工具类型的具体配置
13
- tools?: Record<string, ToolConfig>;
14
-
15
- // MCP 服务器配置
16
- mcp?: {
17
- servers: MCPServerConfig[];
18
- };
19
-
20
- // 提示词配置
21
- prompts?: {
22
- system?: string;
23
- user?: string;
24
- assistant?: string;
25
- };
26
-
27
- // 子代理配置
28
- subAgents?: SubAgentConfig[];
29
-
30
- // 技能配置 (向后兼容)
31
- skills?: {
32
- directories?: string[];
33
- autoScan?: boolean;
34
- cache?: boolean;
35
- };
36
-
37
- // 环境配置
38
- env?: Record<string, string>;
39
-
40
- // 条件配置
41
- conditions?: {
42
- environment?: string;
43
- features?: string[];
44
- };
45
- }
46
-
47
- /**
48
- * 工具配置接口
49
- */
50
- export const ToolConfigSchema = z.object({
51
- type: z.enum(['bash', 'http', 'javascript', 'skill', 'custom']),
52
-
53
- // Bash 工具配置
54
- command: z.string().optional(),
55
- args: z.array(z.string()).optional(),
56
- cwd: z.string().optional(),
57
- env: z.any().optional(),
58
- timeout: z.number().optional(),
59
-
60
- // HTTP 工具配置
61
- method: z.enum(['GET', 'POST', 'PUT', 'DELETE', 'PATCH']).optional(),
62
- url: z.string().optional(),
63
- baseUrl: z.string().optional(),
64
- headers: z.any().optional(),
65
- params: z.any().optional(),
66
- data: z.any().optional(),
67
-
68
- // JavaScript 工具配置
69
- code: z.string().optional(),
70
-
71
- // 通用字段
72
- description: z.string().optional(),
73
- enabled: z.boolean().optional().default(true),
74
-
75
- // 条件字段
76
- conditions: z.object({
77
- environment: z.string().optional(),
78
- platform: z.string().optional()
79
- }).optional()
80
- });
81
-
82
- export type ToolConfig = z.infer<typeof ToolConfigSchema>;
83
-
84
- /**
85
- * MCP 服务器配置接口
86
- */
87
- export const MCPServerConfigSchema = z.object({
88
- name: z.string(),
89
- command: z.string(),
90
- args: z.array(z.string()).optional(),
91
- env: z.any().optional(),
92
- cwd: z.string().optional(),
93
- timeout: z.number().optional(),
94
- description: z.string().optional(),
95
- enabled: z.boolean().optional().default(true)
96
- });
97
-
98
- export type MCPServerConfig = z.infer<typeof MCPServerConfigSchema>;
99
-
100
- /**
101
- * 子代理配置接口
102
- */
103
- export const SubAgentConfigSchema = z.object({
104
- name: z.string(),
105
- trigger: z.array(z.string()),
106
- prompt: z.string(),
107
- model: z.string().optional(),
108
- temperature: z.number().optional(),
109
- maxTokens: z.number().optional(),
110
- tools: z.array(z.string()).optional(),
111
- enabled: z.boolean().optional().default(true)
112
- });
113
-
114
- export type SubAgentConfig = z.infer<typeof SubAgentConfigSchema>;
115
-
116
- /**
117
- * 用户配置插件加载选项
118
- */
119
- export interface UserConfigPluginLoadOptions {
120
- configs?: UserConfigPlugin[];
121
- dirs?: string[];
122
- scan?: boolean;
123
- }
124
-
125
- /**
126
- * 默认用户配置插件目录
127
- */
128
- export const DEFAULT_USER_CONFIG_PLUGIN_DIRS = [
129
- '.pulse-coder/config',
130
- '.coder/config',
131
- '~/.pulse-coder/config',
132
- '~/.coder/config',
133
- './config'
134
- ];
135
-
136
- /**
137
- * 支持的文件格式
138
- */
139
- export const SUPPORTED_CONFIG_FORMATS = ['.json', '.yaml', '.yml'];
140
-
141
- /**
142
- * 配置验证结果
143
- */
144
- export interface ConfigValidationResult {
145
- valid: boolean;
146
- errors?: string[];
147
- warnings?: string[];
148
- }
149
-
150
- /**
151
- * 环境变量替换工具
152
- */
153
- export class ConfigVariableResolver {
154
- private env: Record<string, string>;
155
-
156
- constructor(env: Record<string, string> = {}) {
157
- this.env = env;
158
- }
159
-
160
- resolve(value: string): string {
161
- return value.replace(/\$\{([^}]+)\}/g, (match, varName) => {
162
- const [name, defaultValue] = varName.split(':-');
163
- return this.env[name] || defaultValue || match;
164
- });
165
- }
166
-
167
- resolveObject(obj: any): any {
168
- if (typeof obj === 'string') {
169
- return this.resolve(obj);
170
- }
171
- if (Array.isArray(obj)) {
172
- return obj.map(item => this.resolveObject(item));
173
- }
174
- if (typeof obj === 'object' && obj !== null) {
175
- const resolved: any = {};
176
- for (const [key, value] of Object.entries(obj)) {
177
- resolved[key] = this.resolveObject(value);
178
- }
179
- return resolved;
180
- }
181
- return obj;
182
- }
183
- }
@@ -1 +0,0 @@
1
- export * from './system';