@lark-apaas/nestjs-capability 0.1.3 → 0.1.4-alpha.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.
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/index.ts","../../../../node_modules/tsup/assets/cjs_shims.js","../src/interfaces/error-codes.ts","../src/services/template-engine.service.ts","../src/services/plugin-loader.service.ts","../src/services/capability.service.ts","../src/utils/log-utils.ts","../src/utils/migration-adaptor.ts","../src/controllers/debug.controller.ts","../src/controllers/webhook.controller.ts","../src/capability.module.ts"],"sourcesContent":["export * from './interfaces';\nexport * from './services';\nexport * from './controllers';\nexport * from './capability.module';\nexport { migrationAdaptor, type MigrationResponse, type FailureOutput } from './utils/migration-adaptor';\n","// Shim globals in cjs bundle\n// There's a weird bug that esbuild will always inject importMetaUrl\n// if we export it as `const importMetaUrl = ... __filename ...`\n// But using a function will not cause this issue\n\nconst getImportMetaUrl = () => \n typeof document === \"undefined\" \n ? new URL(`file:${__filename}`).href \n : (document.currentScript && document.currentScript.tagName.toUpperCase() === 'SCRIPT') \n ? document.currentScript.src \n : new URL(\"main.js\", document.baseURI).href;\n\nexport const importMetaUrl = /* @__PURE__ */ getImportMetaUrl()\n","/**\n * Capability 模块错误码\n */\nexport const ErrorCodes = {\n /** 成功 */\n SUCCESS: '0',\n /** 能力不存在 */\n CAPABILITY_NOT_FOUND: 'k_ec_cap_001',\n /** 插件不存在 */\n PLUGIN_NOT_FOUND: 'k_ec_cap_002',\n /** Action 不存在 */\n ACTION_NOT_FOUND: 'k_ec_cap_003',\n /** 参数验证失败 */\n PARAMS_VALIDATION_ERROR: 'k_ec_cap_004',\n /** 执行失败 */\n EXECUTION_ERROR: 'k_ec_cap_005',\n} as const;\n\nexport type ErrorCode = (typeof ErrorCodes)[keyof typeof ErrorCodes];\n","import { Injectable } from '@nestjs/common';\nimport { JSONSchema } from '../interfaces/capability-config.interface';\n\n/**\n * 类型到默认值的映射\n */\nconst TYPE_DEFAULT_VALUES: Record<string, unknown> = {\n array: [],\n object: {},\n string: '',\n number: 0,\n integer: 0,\n boolean: false,\n null: null,\n};\n\n/**\n * 模板引擎服务\n *\n * 支持语法:\n * - expr: '{{' + selector + '}}'\n * - selector: 'input.' + ident | selector.ident\n * - ident: [a-zA-Z_]([a-zA-Z_0-9])*\n *\n * 示例:\n * - {{input.a}}\n * - {{input.a.b}}\n * - \"this is {{input.a.b}}\"\n *\n * 求值规则:\n * - 如果整个字符串是单个表达式,保留原始类型\n * - 如果是字符串插值(多个表达式或混合内容),返回字符串\n * - 如果变量不存在:\n * - 未传 schema:返回原始表达式\n * - 传了 schema(整串表达式):按 schema 返回默认值\n * - 传了 schema(字符串插值):保留原始表达式\n */\n@Injectable()\nexport class TemplateEngineService {\n // 匹配 {{input.xxx}} 或 {{input.xxx.yyy}} 表达式\n private readonly EXPR_REGEX =\n /\\{\\{\\s*input\\.([a-zA-Z_][a-zA-Z_0-9]*(?:\\.[a-zA-Z_][a-zA-Z_0-9]*)*\\s*)\\}\\}/g;\n // 匹配整串单个表达式\n private readonly WHOLE_STRING_EXPR_REGEX =\n /^\\{\\{\\s*input\\.([a-zA-Z_][a-zA-Z_0-9]*(?:\\.[a-zA-Z_][a-zA-Z_0-9]*)*)\\s*\\}\\}$/;\n\n /**\n * 解析 formValue 模板\n * @param template - formValue 模板对象\n * @param input - 用户输入参数\n * @param paramsSchema - 可选,输入参数的 JSON Schema,用于推断默认值\n * @returns 解析后的参数对象\n */\n resolve(\n template: unknown,\n input: Record<string, unknown>,\n paramsSchema?: JSONSchema,\n ): unknown {\n if (typeof template === 'string') {\n return this.resolveString(template, input, paramsSchema);\n }\n\n if (Array.isArray(template)) {\n return template.map(item => this.resolve(item, input, paramsSchema));\n }\n\n if (template !== null && typeof template === 'object') {\n return this.resolveObject(\n template as Record<string, unknown>,\n input,\n paramsSchema,\n );\n }\n\n return template;\n }\n\n private resolveString(\n template: string,\n input: Record<string, unknown>,\n paramsSchema?: JSONSchema,\n ): unknown {\n // 情况1: 整串是单个表达式 → 保留原始类型\n const wholeMatch = template.match(this.WHOLE_STRING_EXPR_REGEX);\n if (wholeMatch) {\n const path = wholeMatch[1];\n const value = this.getValueByPath(input, path);\n\n // 变量存在,返回值\n if (value !== undefined) {\n return value;\n }\n\n // 变量不存在,尝试从 schema 获取默认值\n if (paramsSchema) {\n const defaultValue = this.getDefaultValueForPath(path, paramsSchema);\n if (defaultValue !== undefined) {\n return defaultValue;\n }\n }\n\n // 回退:返回原始表达式\n return template;\n }\n\n // 情况2: 字符串插值 → 返回字符串(不使用 schema 默认值)\n // 重置正则的 lastIndex(因为使用了 g 标志)\n this.EXPR_REGEX.lastIndex = 0;\n\n // 检查是否有任何表达式\n if (!this.EXPR_REGEX.test(template)) {\n return template;\n }\n\n // 重置并进行替换\n this.EXPR_REGEX.lastIndex = 0;\n const result = template.replace(this.EXPR_REGEX, (match, path) => {\n const value = this.getValueByPath(input, path);\n // 变量不存在,保留原始表达式\n if (value === undefined) {\n return match;\n }\n return String(value);\n });\n\n return result;\n }\n\n private resolveObject(\n template: Record<string, unknown>,\n input: Record<string, unknown>,\n paramsSchema?: JSONSchema,\n ): Record<string, unknown> {\n const result: Record<string, unknown> = {};\n\n for (const [key, value] of Object.entries(template)) {\n result[key] = this.resolve(value, input, paramsSchema);\n }\n\n return result;\n }\n\n private getValueByPath(obj: Record<string, unknown>, path: string): unknown {\n const keys = path.split('.');\n let current: unknown = obj;\n\n for (const key of keys) {\n if (current === null || current === undefined) {\n return undefined;\n }\n current = (current as Record<string, unknown>)[key];\n }\n\n return current;\n }\n\n /**\n * 根据路径从 schema 获取默认值\n * @param path - 变量路径,如 \"a.b.c\"\n * @param schema - JSON Schema\n * @returns 默认值,如果无法确定则返回 undefined\n */\n private getDefaultValueForPath(\n path: string,\n schema: JSONSchema,\n ): unknown {\n const fieldSchema = this.getSchemaForPath(path, schema);\n if (!fieldSchema) {\n return undefined;\n }\n return this.getDefaultValueFromSchema(fieldSchema);\n }\n\n /**\n * 根据路径查找对应的 schema 定义\n * @param path - 变量路径,如 \"a.b.c\"\n * @param schema - 根 JSON Schema\n * @returns 路径对应的 schema,如果不存在则返回 undefined\n */\n private getSchemaForPath(\n path: string,\n schema: JSONSchema,\n ): JSONSchema | undefined {\n const keys = path.split('.');\n let currentSchema: JSONSchema | undefined = schema;\n\n for (const key of keys) {\n if (!currentSchema?.properties) {\n return undefined;\n }\n currentSchema = currentSchema.properties[key];\n if (!currentSchema) {\n return undefined;\n }\n }\n\n return currentSchema;\n }\n\n /**\n * 从 schema 获取默认值\n * 优先级:default > type > undefined\n * @param schema - 字段的 JSON Schema\n * @returns 默认值\n */\n private getDefaultValueFromSchema(schema: JSONSchema): unknown {\n // 优先使用 schema 中定义的 default\n if (schema.default !== undefined) {\n return schema.default;\n }\n\n // 如果有 oneOf 或 anyOf,无法确定类型,返回 undefined\n if (schema.oneOf || schema.anyOf) {\n return undefined;\n }\n\n // 根据 type 返回默认值\n const type = schema.type;\n if (!type) {\n return undefined;\n }\n\n // 联合类型:取第一个类型的默认值\n if (Array.isArray(type)) {\n for (const t of type) {\n if (t in TYPE_DEFAULT_VALUES) {\n return TYPE_DEFAULT_VALUES[t];\n }\n }\n return undefined;\n }\n\n // 单一类型\n return TYPE_DEFAULT_VALUES[type];\n }\n}\n","import { Injectable, Logger } from '@nestjs/common';\nimport { createRequire } from 'node:module';\nimport { pathToFileURL } from 'node:url';\nimport type { PluginInstance, PluginPackage } from '../interfaces';\n\n// 社区标准模式:tsup shims 会为 CJS 输出自动处理 import.meta.url\nconst require = createRequire(import.meta.url);\n\nexport class PluginNotFoundError extends Error {\n readonly pluginKey: string;\n\n constructor(pluginKey: string) {\n super(`Plugin not found: ${pluginKey}`);\n this.name = 'PluginNotFoundError';\n this.pluginKey = pluginKey;\n }\n}\n\nexport class PluginLoadError extends Error {\n constructor(pluginKey: string, reason: string) {\n super(`Failed to load plugin ${pluginKey}: ${reason}`);\n this.name = 'PluginLoadError';\n }\n}\n\n@Injectable()\nexport class PluginLoaderService {\n private readonly logger = new Logger(PluginLoaderService.name);\n private readonly pluginInstances = new Map<string, PluginInstance>();\n /** 记录每个插件的加载版本(时间戳),用于 ESM 缓存绕过 */\n private readonly pluginVersions = new Map<string, number>();\n\n async loadPlugin(pluginKey: string): Promise<PluginInstance> {\n const cached = this.pluginInstances.get(pluginKey);\n if (cached) {\n this.logger.debug(`Using cached plugin instance: ${pluginKey}`);\n return cached;\n }\n\n this.logger.log(`Loading plugin: ${pluginKey}`);\n\n try {\n // 1. 将包名解析为实际文件路径\n const resolvedPath = require.resolve(pluginKey);\n\n // 2. 使用 file:// URL + 时间戳绕过 ESM 缓存\n const version = this.pluginVersions.get(pluginKey) ?? 0;\n const fileUrl = pathToFileURL(resolvedPath).href;\n const importPath = version > 0 ? `${fileUrl}?v=${version}` : fileUrl;\n\n // 3. 动态导入(支持 ESM 和 CJS 插件)\n const mod = await import(importPath);\n const pluginPackage = (mod.default ?? mod) as PluginPackage;\n\n if (typeof pluginPackage.create !== 'function') {\n throw new PluginLoadError(pluginKey, 'Plugin does not export create() function');\n }\n\n const instance = pluginPackage.create();\n this.pluginInstances.set(pluginKey, instance);\n\n this.logger.log(`Plugin loaded successfully: ${pluginKey}`);\n return instance;\n } catch (error) {\n if ((error as NodeJS.ErrnoException).code === 'MODULE_NOT_FOUND') {\n throw new PluginNotFoundError(pluginKey);\n }\n throw new PluginLoadError(\n pluginKey,\n error instanceof Error ? error.message : String(error),\n );\n }\n }\n\n isPluginInstalled(pluginKey: string): boolean {\n try {\n require.resolve(pluginKey);\n return true;\n } catch {\n return false;\n }\n }\n\n /**\n * 清除插件缓存\n * - 清除应用层 pluginInstances 缓存\n * - 清除 Node.js CJS 模块缓存(require.cache)\n * - 更新版本号,下次 import 时绕过 ESM 缓存\n * @param pluginKey - 插件标识,不传则清除所有\n */\n clearCache(pluginKey?: string): void {\n if (pluginKey) {\n this.pluginInstances.delete(pluginKey);\n this.clearNodeModuleCache(pluginKey);\n // 更新版本号,下次 import 时使用新的 URL 绕过 ESM 缓存\n this.pluginVersions.set(pluginKey, Date.now());\n this.logger.log(`Cleared cache for plugin: ${pluginKey}`);\n } else {\n // 清除所有插件\n for (const key of this.pluginInstances.keys()) {\n this.clearNodeModuleCache(key);\n this.pluginVersions.set(key, Date.now());\n }\n this.pluginInstances.clear();\n this.logger.log('Cleared all plugin caches');\n }\n }\n\n /**\n * 清除 CJS 模块缓存\n */\n private clearNodeModuleCache(pluginKey: string): void {\n try {\n const resolvedPath = require.resolve(pluginKey);\n\n // 清除主模块及其依赖\n const mod = require.cache[resolvedPath];\n if (mod) {\n this.clearModuleAndChildren(mod, pluginKey);\n }\n\n delete require.cache[resolvedPath];\n } catch {\n // 插件可能未安装或是纯 ESM 模块,忽略错误\n }\n }\n\n /**\n * 递归清除子模块缓存\n */\n private clearModuleAndChildren(mod: NodeModule, pluginKey: string): void {\n // 只清除插件目录下的模块,避免清除核心模块\n const pluginScope = pluginKey.startsWith('@') ? pluginKey.split('/').slice(0, 2).join('/') : pluginKey;\n\n mod.children.forEach(child => {\n if (child.filename.includes(`node_modules/${pluginScope}`)) {\n this.clearModuleAndChildren(child, pluginKey);\n delete require.cache[child.filename];\n }\n });\n }\n\n /**\n * 强制重新加载插件\n * @param pluginKey - 插件标识\n */\n async reloadPlugin(pluginKey: string): Promise<PluginInstance> {\n this.clearCache(pluginKey);\n return this.loadPlugin(pluginKey);\n }\n}\n","import { Injectable, Logger, Inject, OnModuleInit, OnModuleDestroy } from '@nestjs/common';\nimport {\n RequestContextService,\n PLATFORM_HTTP_CLIENT,\n type PlatformHttpClient,\n} from '@lark-apaas/nestjs-common';\nimport * as fs from 'node:fs';\nimport * as path from 'node:path';\nimport * as chokidar from 'chokidar';\nimport type {\n CapabilityConfig,\n PluginActionContext,\n UserContext,\n StreamEvent,\n} from '../interfaces';\nimport { PluginLoaderService } from './plugin-loader.service';\nimport { TemplateEngineService } from './template-engine.service';\nimport { stringifyForLog } from '../utils';\n\nexport class CapabilityNotFoundError extends Error {\n readonly capabilityId: string;\n\n constructor(capabilityId: string) {\n super(`Capability not found: ${capabilityId}`);\n this.name = 'CapabilityNotFoundError';\n this.capabilityId = capabilityId;\n }\n}\n\nexport class ActionNotFoundError extends Error {\n readonly pluginKey: string;\n readonly actionName: string;\n\n constructor(pluginKey: string, actionName: string) {\n super(`Action '${actionName}' not found in plugin ${pluginKey}`);\n this.name = 'ActionNotFoundError';\n this.pluginKey = pluginKey;\n this.actionName = actionName;\n }\n}\n\nexport interface CapabilityExecutor {\n /**\n * 调用 capability(始终返回 Promise)\n * - unary action: 直接返回结果\n * - stream action: 内部聚合所有 chunk 后返回\n */\n call(actionName: string, input: unknown, context?: Partial<PluginActionContext>): Promise<unknown>;\n\n /**\n * 流式调用 capability,返回原始流\n * - 返回原始 AsyncIterable\n * - 如果 action 是 unary,包装为单次 yield\n */\n callStream(actionName: string, input: unknown, context?: Partial<PluginActionContext>): AsyncIterable<unknown>;\n\n /**\n * 流式调用 capability,返回带事件协议的流(推荐)\n * - 返回 StreamEvent 类型的 AsyncIterable\n * - 支持 data/done/error 三种事件类型\n * - Controller 层应优先使用此方法实现边收边发\n */\n callStreamWithEvents(\n actionName: string,\n input: unknown,\n context?: Partial<PluginActionContext>,\n ): AsyncIterable<StreamEvent<unknown>>;\n\n /**\n * 检查 action 是否为流式\n */\n isStream(actionName: string): Promise<boolean>;\n}\n\nexport interface CapabilityModuleOptions {\n /** 能力配置目录路径,默认 server/capabilities */\n capabilitiesDir?: string;\n /** 是否启用文件监听(热更新),默认 false */\n enableWatching?: boolean;\n /** 文件变更防抖时间(ms),默认 300 */\n watchDebounce?: number;\n}\n\n@Injectable()\nexport class CapabilityService implements OnModuleInit, OnModuleDestroy {\n private readonly logger = new Logger(CapabilityService.name);\n private readonly capabilities = new Map<string, CapabilityConfig>();\n /** 文件路径到 capability id 的映射,用于文件删除时查找 */\n private readonly filePathToId = new Map<string, string>();\n private capabilitiesDir: string;\n private fileWatcher: chokidar.FSWatcher | null = null;\n private options: CapabilityModuleOptions = {};\n\n constructor(\n private readonly requestContextService: RequestContextService,\n @Inject(PLATFORM_HTTP_CLIENT) private readonly platformHttpClient: PlatformHttpClient,\n private readonly pluginLoaderService: PluginLoaderService,\n private readonly templateEngineService: TemplateEngineService,\n ) {\n const isDev = process.env.NODE_ENV === 'development';\n this.capabilitiesDir = path.join(process.cwd(), isDev ? 'server/capabilities' : 'capabilities');\n }\n\n /**\n * 设置模块配置\n */\n setOptions(options: CapabilityModuleOptions): void {\n this.options = options;\n if (options.capabilitiesDir) {\n this.capabilitiesDir = options.capabilitiesDir;\n }\n }\n\n setCapabilitiesDir(dir: string): void {\n this.capabilitiesDir = dir;\n }\n\n async onModuleInit(): Promise<void> {\n await this.loadCapabilities();\n\n // 如果启用文件监听,开始监听\n if (this.options.enableWatching) {\n this.startWatching();\n }\n }\n\n async onModuleDestroy(): Promise<void> {\n this.stopWatching();\n }\n\n // ==================== 文件监听相关方法 ====================\n\n /**\n * 启动文件监听(沙箱环境自动调用)\n */\n startWatching(): void {\n if (this.fileWatcher) {\n return; // 已在监听\n }\n\n if (!fs.existsSync(this.capabilitiesDir)) {\n this.logger.warn(`Cannot start watching: directory not found: ${this.capabilitiesDir}`);\n return;\n }\n\n const pattern = path.join(this.capabilitiesDir, '*.json');\n const debounce = this.options.watchDebounce ?? 300;\n\n this.logger.log(`Starting file watcher: ${pattern}`);\n\n this.fileWatcher = chokidar.watch(pattern, {\n persistent: true,\n ignoreInitial: true, // 忽略初始扫描\n awaitWriteFinish: {\n // 等待写入完成\n stabilityThreshold: debounce,\n pollInterval: 100,\n },\n });\n\n this.fileWatcher\n .on('add', filePath => this.handleFileAdd(filePath))\n .on('change', filePath => this.handleFileChange(filePath))\n .on('unlink', filePath => this.handleFileUnlink(filePath))\n .on('error', error => this.logger.error('File watcher error:', error));\n\n this.logger.log('File watcher started');\n }\n\n /**\n * 停止文件监听\n */\n stopWatching(): void {\n if (this.fileWatcher) {\n this.fileWatcher.close();\n this.fileWatcher = null;\n this.logger.log('File watcher stopped');\n }\n }\n\n /**\n * 重新加载所有能力配置\n */\n async reloadAllCapabilities(): Promise<void> {\n this.capabilities.clear();\n this.filePathToId.clear();\n await this.loadCapabilities();\n }\n\n private handleFileAdd(filePath: string): void {\n this.logger.log(`Capability file added: ${filePath}`);\n try {\n this.loadCapabilityFromFile(filePath);\n\n // 清除关联插件的缓存,确保加载最新版本\n const capabilityId = this.filePathToId.get(filePath);\n if (capabilityId) {\n const config = this.capabilities.get(capabilityId);\n if (config?.pluginKey) {\n this.pluginLoaderService.clearCache(config.pluginKey);\n this.logger.log(`Cleared plugin cache for new capability: ${config.pluginKey}`);\n }\n }\n } catch (error) {\n this.logger.error(`Failed to load new capability: ${filePath}`, error);\n }\n }\n\n private handleFileChange(filePath: string): void {\n this.logger.log(`Capability file changed: ${filePath}`);\n try {\n this.reloadCapabilityFromFile(filePath);\n } catch (error) {\n this.logger.error(`Failed to reload capability: ${filePath}`, error);\n }\n }\n\n private handleFileUnlink(filePath: string): void {\n this.logger.log(`Capability file removed: ${filePath}`);\n this.removeCapabilityByFile(filePath);\n }\n\n /**\n * 从文件加载单个能力配置\n */\n private loadCapabilityFromFile(filePath: string): void {\n const content = fs.readFileSync(filePath, 'utf-8');\n const config = JSON.parse(content) as CapabilityConfig;\n\n if (!config.id) {\n this.logger.warn(`Skipping capability without id: ${filePath}`);\n return;\n }\n\n this.capabilities.set(config.id, config);\n this.filePathToId.set(filePath, config.id);\n this.logger.log(`Loaded capability: ${config.id} (${config.name})`);\n }\n\n /**\n * 重新加载单个能力配置\n */\n private reloadCapabilityFromFile(filePath: string): void {\n // 先移除旧配置(如果存在)\n const oldId = this.filePathToId.get(filePath);\n if (oldId) {\n const oldConfig = this.capabilities.get(oldId);\n if (oldConfig) {\n // 清除关联插件的缓存\n this.pluginLoaderService.clearCache(oldConfig.pluginKey);\n }\n this.capabilities.delete(oldId);\n }\n\n // 重新加载\n this.loadCapabilityFromFile(filePath);\n }\n\n /**\n * 根据文件路径移除能力配置\n */\n private removeCapabilityByFile(filePath: string): void {\n const capabilityId = this.filePathToId.get(filePath);\n if (capabilityId) {\n this.capabilities.delete(capabilityId);\n this.filePathToId.delete(filePath);\n this.logger.log(`Removed capability: ${capabilityId}`);\n }\n }\n\n // ==================== 原有方法 ====================\n\n private async loadCapabilities(): Promise<void> {\n this.logger.log(`Loading capabilities from ${this.capabilitiesDir}`);\n\n if (!fs.existsSync(this.capabilitiesDir)) {\n this.logger.warn(`Capabilities directory not found: ${this.capabilitiesDir}`);\n return;\n }\n\n const files = fs.readdirSync(this.capabilitiesDir).filter(f => f.endsWith('.json'));\n\n for (const file of files) {\n try {\n const filePath = path.join(this.capabilitiesDir, file);\n this.loadCapabilityFromFile(filePath);\n } catch (error) {\n this.logger.error(`Failed to load capability from ${file}:`, error);\n }\n }\n\n this.logger.log(`Loaded ${this.capabilities.size} capabilities`);\n }\n\n listCapabilities(): CapabilityConfig[] {\n return Array.from(this.capabilities.values());\n }\n\n getCapability(capabilityId: string): CapabilityConfig | null {\n return this.capabilities.get(capabilityId) ?? null;\n }\n\n load(capabilityId: string): CapabilityExecutor {\n const config = this.capabilities.get(capabilityId);\n if (!config) {\n throw new CapabilityNotFoundError(capabilityId);\n }\n\n return this.createExecutor(config);\n }\n\n /**\n * 使用传入的配置加载能力执行器\n * 用于 debug 场景,支持用户传入自定义配置\n */\n loadWithConfig(config: CapabilityConfig): CapabilityExecutor {\n return this.createExecutor(config);\n }\n\n private createExecutor(config: CapabilityConfig): CapabilityExecutor {\n return {\n call: async (\n actionName: string,\n input: unknown,\n contextOverride?: Partial<PluginActionContext>,\n ) => {\n return this.executeCall(config, actionName, input, contextOverride);\n },\n\n callStream: (\n actionName: string,\n input: unknown,\n contextOverride?: Partial<PluginActionContext>,\n ) => {\n return this.executeCallStream(config, actionName, input, contextOverride);\n },\n\n callStreamWithEvents: (\n actionName: string,\n input: unknown,\n contextOverride?: Partial<PluginActionContext>,\n ) => {\n return this.executeCallStreamWithEvents(config, actionName, input, contextOverride);\n },\n\n isStream: async (actionName: string) => {\n return this.checkIsStream(config, actionName);\n },\n };\n }\n\n /**\n * 检查 action 是否为流式\n */\n private async checkIsStream(config: CapabilityConfig, actionName: string): Promise<boolean> {\n const pluginInstance = await this.pluginLoaderService.loadPlugin(config.pluginKey);\n\n if (!pluginInstance.hasAction(actionName)) {\n throw new ActionNotFoundError(config.pluginKey, actionName);\n }\n\n return pluginInstance.isStreamAction?.(actionName) ?? false;\n }\n\n /**\n * 执行 capability(始终返回 Promise)\n * - unary action: 直接返回结果\n * - stream action: 内部聚合所有 chunk 后返回\n */\n private async executeCall(\n config: CapabilityConfig,\n actionName: string,\n input: unknown,\n contextOverride?: Partial<PluginActionContext>,\n ): Promise<unknown> {\n const startTime = Date.now();\n const loggerContext = {\n capability_id: config.id,\n plugin_key: config.pluginKey,\n action: actionName,\n };\n try {\n const pluginInstance = await this.pluginLoaderService.loadPlugin(config.pluginKey);\n\n if (!pluginInstance.hasAction(actionName)) {\n throw new ActionNotFoundError(config.pluginKey, actionName);\n }\n\n // 如果 formValue 不存在,直接使用 input\n const resolvedParams = config.formValue\n ? this.templateEngineService.resolve(config.formValue, input as Record<string, unknown>, config.paramsSchema)\n : input;\n\n const context = this.buildActionContext(contextOverride);\n const isStream = pluginInstance.isStreamAction?.(actionName) ?? false;\n\n // 打印入参\n this.logger.log('Executing capability (call)', {\n ...loggerContext,\n is_stream: isStream,\n input: stringifyForLog(input),\n });\n\n let result: unknown;\n\n if (isStream && pluginInstance.runStream) {\n // 流式 action:聚合所有 chunk\n const chunks: unknown[] = [];\n for await (const chunk of pluginInstance.runStream(actionName, context, resolvedParams)) {\n chunks.push(chunk);\n }\n // 使用插件的聚合方法,或默认返回 chunks 数组\n result = pluginInstance.aggregate\n ? pluginInstance.aggregate(actionName, chunks)\n : chunks;\n } else {\n // 非流式 action:直接调用\n result = await pluginInstance.run(actionName, context, resolvedParams);\n }\n\n // 打印返回值\n this.logger.log('Capability (call) executed successfully', {\n ...loggerContext,\n duration_ms: Date.now() - startTime,\n output: stringifyForLog(result),\n });\n\n return result;\n } catch (error) {\n this.logger.error('Capability (call) execution failed', {\n ...loggerContext,\n duration_ms: Date.now() - startTime,\n error: error instanceof Error ? error.message : String(error),\n });\n throw error;\n }\n }\n\n /**\n * 流式执行 capability\n * - stream action: 返回原始 AsyncIterable\n * - unary action: 包装为单次 yield\n */\n private async *executeCallStream(\n config: CapabilityConfig,\n actionName: string,\n input: unknown,\n contextOverride?: Partial<PluginActionContext>,\n ): AsyncIterable<unknown> {\n const startTime = Date.now();\n const loggerContext = {\n capability_id: config.id,\n plugin_key: config.pluginKey,\n action: actionName,\n };\n const chunks: unknown[] = [];\n try {\n const pluginInstance = await this.pluginLoaderService.loadPlugin(config.pluginKey);\n\n if (!pluginInstance.hasAction(actionName)) {\n throw new ActionNotFoundError(config.pluginKey, actionName);\n }\n\n // 如果 formValue 不存在,直接使用 input\n const resolvedParams = config.formValue\n ? this.templateEngineService.resolve(config.formValue, input as Record<string, unknown>, config.paramsSchema)\n : input;\n\n const context = this.buildActionContext(contextOverride);\n const isStream = pluginInstance.isStreamAction?.(actionName) ?? false;\n\n // 打印入参\n this.logger.log('Executing capability (stream)', {\n ...loggerContext,\n is_stream: isStream,\n input: stringifyForLog(input),\n });\n\n if (isStream && pluginInstance.runStream) {\n // 流式 action:透传 AsyncIterable,同时收集 chunks\n for await (const chunk of pluginInstance.runStream(actionName, context, resolvedParams)) {\n chunks.push(chunk);\n yield chunk;\n }\n } else {\n // 非流式 action:包装为单次 yield\n const result = await pluginInstance.run(actionName, context, resolvedParams);\n chunks.push(result);\n yield result;\n }\n\n // 打印聚合结果\n const aggregatedResult = pluginInstance.aggregate\n ? pluginInstance.aggregate(actionName, chunks)\n : chunks;\n this.logger.log('Capability (stream) executed successfully', {\n ...loggerContext,\n duration_ms: Date.now() - startTime,\n output: stringifyForLog(aggregatedResult),\n });\n } catch (error) {\n this.logger.error('Capability (stream) execution failed', {\n ...loggerContext,\n duration_ms: Date.now() - startTime,\n error: error instanceof Error ? error.message : String(error),\n });\n throw error;\n }\n }\n\n /**\n * 流式执行 capability,返回带事件协议的流\n * - 优先使用 pluginInstance.runStreamWithEvents\n * - 如果插件不支持,则包装 runStream/run 为 StreamEvent\n */\n private async *executeCallStreamWithEvents(\n config: CapabilityConfig,\n actionName: string,\n input: unknown,\n contextOverride?: Partial<PluginActionContext>,\n ): AsyncIterable<StreamEvent<unknown>> {\n const startTime = Date.now();\n const loggerContext = {\n capability_id: config.id,\n plugin_key: config.pluginKey,\n action: actionName,\n };\n const chunks: unknown[] = [];\n try {\n const pluginInstance = await this.pluginLoaderService.loadPlugin(config.pluginKey);\n\n if (!pluginInstance.hasAction(actionName)) {\n throw new ActionNotFoundError(config.pluginKey, actionName);\n }\n\n // 如果 formValue 不存在,直接使用 input\n const resolvedParams = config.formValue\n ? this.templateEngineService.resolve(config.formValue, input as Record<string, unknown>, config.paramsSchema)\n : input;\n\n const context = this.buildActionContext(contextOverride);\n const isStream = pluginInstance.isStreamAction?.(actionName) ?? false;\n\n // 打印入参\n this.logger.log('Executing capability (streamWithEvents)', {\n ...loggerContext,\n is_stream: isStream,\n input: stringifyForLog(input),\n });\n\n // 优先使用 runStreamWithEvents\n if (pluginInstance.runStreamWithEvents) {\n for await (const event of pluginInstance.runStreamWithEvents(actionName, context, resolvedParams)) {\n // 收集 data 事件的数据用于聚合\n if (event.type === 'data') {\n chunks.push(event.data);\n yield event;\n } else if (event.type === 'done') {\n // 拦截 done 事件,添加聚合结果\n const aggregatedResult = pluginInstance.aggregate\n ? pluginInstance.aggregate(actionName, chunks)\n : chunks;\n yield {\n type: 'done',\n metadata: {\n ...event.metadata,\n aggregated: aggregatedResult,\n },\n };\n } else {\n yield event;\n }\n }\n } else {\n // 回退:包装 runStream 或 run 为 StreamEvent\n if (isStream && pluginInstance.runStream) {\n // 流式 action:包装每个 chunk 为 data 事件\n for await (const chunk of pluginInstance.runStream(actionName, context, resolvedParams)) {\n chunks.push(chunk);\n yield { type: 'data', data: chunk };\n }\n } else {\n // 非流式 action:包装结果为单个 data 事件\n const result = await pluginInstance.run(actionName, context, resolvedParams);\n chunks.push(result);\n yield { type: 'data', data: result };\n }\n\n // 聚合结果\n const aggregatedResult = pluginInstance.aggregate\n ? pluginInstance.aggregate(actionName, chunks)\n : chunks;\n\n // 发送 done 事件(包含聚合结果)\n yield {\n type: 'done',\n metadata: {\n chunks: chunks.length,\n duration: Date.now() - startTime,\n aggregated: aggregatedResult,\n },\n };\n }\n\n // 打印聚合结果\n const aggregatedResult = pluginInstance.aggregate\n ? pluginInstance.aggregate(actionName, chunks)\n : chunks;\n this.logger.log('Capability (streamWithEvents) executed successfully', {\n ...loggerContext,\n duration_ms: Date.now() - startTime,\n output: stringifyForLog(aggregatedResult),\n });\n } catch (error) {\n this.logger.error('Capability (streamWithEvents) execution failed', {\n ...loggerContext,\n duration_ms: Date.now() - startTime,\n error: error instanceof Error ? error.message : String(error),\n });\n\n // 发送 error 事件\n yield {\n type: 'error',\n error: {\n code: 'EXECUTION_ERROR',\n message: error instanceof Error ? error.message : String(error),\n },\n };\n }\n }\n\n private buildActionContext(override?: Partial<PluginActionContext>): PluginActionContext {\n return {\n logger: this.logger,\n platformHttpClient: this.platformHttpClient,\n userContext: override?.userContext ?? this.getUserContext(),\n isDebug: override?.isDebug ?? false,\n };\n }\n\n private getUserContext(): UserContext {\n const ctx = this.requestContextService.getContext();\n return {\n appId: ctx?.appId ?? '',\n userId: ctx?.userId ?? '',\n tenantId: ctx?.tenantId ?? '',\n };\n }\n}\n","/**\n * 日志工具函数\n */\n\nconst DEFAULT_MAX_LENGTH = 1000;\n\n/**\n * 截断字符串\n */\nfunction truncateString(str: string, maxLength: number): string {\n if (str.length <= maxLength) {\n return str;\n }\n return str.slice(0, maxLength) + `... [truncated, total ${str.length} chars]`;\n}\n\n/**\n * 将对象转换为可打印的字符串,超过指定长度时截断\n * @param value - 要打印的值\n * @param maxLength - 最大字符数,默认 1000\n */\nexport function stringifyForLog(value: unknown, maxLength: number = DEFAULT_MAX_LENGTH): string {\n try {\n const str = JSON.stringify(value, null, 2);\n return truncateString(str, maxLength);\n } catch {\n // 处理循环引用等情况\n const str = String(value);\n return truncateString(str, maxLength);\n }\n}\n","/**\n * Migration Adaptor\n *\n * 用于迁移老版本 capability 调用代码,将新版本的返回结果包装为老版本的 Response 结构\n */\n\n/**\n * 失败输出结构\n */\nexport interface FailureOutput {\n response: {\n status: {\n /** 协议层返回码(如 HTTP 状态码) */\n protocolStatusCode: string;\n /** 业务应用状态码 */\n appStatusCode: string;\n };\n /** 返回体(JSON 字符串形式) */\n body: string;\n };\n}\n\n/**\n * 老版本 capability 调用的 Response 结构\n *\n * 注意:output 使用 any 类型以简化迁移代码,避免类型断言\n */\nexport interface MigrationResponse {\n /** 当 code=0 时表示执行成功,当 code!=0 时表示执行失败 */\n code: number;\n data?: {\n /** 执行成功时的输出结果(any 类型,兼容迁移场景) */\n output?: any;\n /** 执行结果,'success' 或 'error' */\n outcome?: 'success' | 'error';\n /** 执行失败时的输出结果 */\n failureOutput?: FailureOutput;\n };\n /** 发生错误时的错误信息 */\n message?: string;\n}\n\n/**\n * 迁移适配器\n *\n * 将 CapabilityService.load().call() 的返回结果包装为老版本的 Response 结构\n *\n * @example\n * // 老代码\n * const result = await generateTaskDescription(params);\n *\n * // 新代码\n * const result = await migrationAdaptor(\n * this.capabilityService.load('ai_generate_task_description').call('run', params)\n * );\n *\n * // result 结构\n * // {\n * // code: 0,\n * // data: {\n * // output: { ... }, // 能力执行结果\n * // outcome: 'success'\n * // }\n * // }\n */\nexport async function migrationAdaptor(\n promise: Promise<unknown>,\n): Promise<MigrationResponse> {\n try {\n const result = await promise;\n\n return {\n code: 0,\n data: {\n output: result,\n outcome: 'success',\n },\n };\n } catch (error) {\n const errorMessage = error instanceof Error ? error.message : String(error);\n const appStatusCode = error instanceof Error && 'code' in error ? String(error.code) : 'EXECUTION_ERROR';\n\n return {\n code: -1,\n data: {\n outcome: 'error',\n failureOutput: {\n response: {\n status: {\n protocolStatusCode: '500',\n appStatusCode,\n },\n body: JSON.stringify({\n error: errorMessage,\n stack: error instanceof Error ? error.stack : undefined,\n }),\n },\n },\n },\n message: errorMessage,\n };\n }\n}\n","import {\n Controller,\n Post,\n Get,\n Param,\n Body,\n Res,\n HttpStatus,\n Logger,\n} from '@nestjs/common';\nimport { ApiExcludeController } from '@nestjs/swagger';\nimport type { Response } from 'express';\nimport {\n CapabilityService,\n CapabilityNotFoundError,\n ActionNotFoundError,\n} from '../services/capability.service';\nimport { PluginLoaderService, PluginNotFoundError } from '../services/plugin-loader.service';\nimport { TemplateEngineService } from '../services/template-engine.service';\nimport type {\n CapabilityConfig,\n SuccessResponse,\n ErrorResponse,\n DebugExecuteResponseData,\n ListResponseData,\n StreamContentResponse,\n StreamErrorResponse,\n} from '../interfaces';\nimport { ErrorCodes } from '../interfaces';\nimport { stringifyForLog } from '../utils';\n\ninterface DebugRequestBody {\n action?: string;\n params?: Record<string, unknown>;\n capability?: CapabilityConfig;\n}\n\n@ApiExcludeController()\n@Controller('__innerapi__/capability')\nexport class DebugController {\n private readonly logger = new Logger(DebugController.name);\n\n constructor(\n private readonly capabilityService: CapabilityService,\n private readonly pluginLoaderService: PluginLoaderService,\n private readonly templateEngineService: TemplateEngineService,\n ) {}\n\n @Get('list')\n list(@Res() res: Response): void {\n try {\n const capabilities = this.capabilityService.listCapabilities();\n\n res.status(HttpStatus.OK).json({\n status_code: ErrorCodes.SUCCESS,\n data: {\n capabilities: capabilities.map(c => ({\n id: c.id,\n name: c.name,\n pluginID: c.pluginKey,\n pluginVersion: c.pluginVersion,\n })),\n },\n } satisfies SuccessResponse<ListResponseData>);\n } catch (error) {\n this.logger.error('Failed to list capabilities', error);\n res.status(HttpStatus.INTERNAL_SERVER_ERROR).json({\n status_code: ErrorCodes.EXECUTION_ERROR,\n error_msg: `Failed to list capabilities: ${error instanceof Error ? error.message : String(error)}`,\n } satisfies ErrorResponse);\n }\n }\n\n /**\n * 获取 capability 配置\n * 优先使用 body.capability,否则从服务获取\n */\n private getCapabilityConfig(\n capabilityId: string,\n bodyCapability?: CapabilityConfig,\n ): CapabilityConfig {\n if (bodyCapability) {\n return bodyCapability;\n }\n\n const config = this.capabilityService.getCapability(capabilityId);\n if (!config) {\n throw new CapabilityNotFoundError(capabilityId);\n }\n\n return config;\n }\n\n /**\n * 获取 action 名称\n * 优先使用传入的 action,否则使用插件第一个 action\n */\n private async getActionName(pluginKey: string, bodyAction?: string): Promise<string> {\n if (bodyAction) {\n return bodyAction;\n }\n\n const pluginInstance = await this.pluginLoaderService.loadPlugin(pluginKey);\n const actions = pluginInstance.listActions();\n\n if (actions.length === 0) {\n throw new Error(`Plugin ${pluginKey} has no actions`);\n }\n\n return actions[0];\n }\n\n @Post('debug/:capability_id')\n async debug(\n @Param('capability_id') capabilityId: string,\n @Body() body: DebugRequestBody,\n @Res() res: Response,\n ): Promise<void> {\n const startTime = Date.now();\n const params = body.params ?? {};\n\n try {\n const config = this.getCapabilityConfig(capabilityId, body.capability);\n const action = await this.getActionName(config.pluginKey, body.action);\n\n const resolvedParams = this.templateEngineService.resolve(\n config.formValue,\n params,\n config.paramsSchema,\n );\n\n const result = await this.capabilityService\n .loadWithConfig(config)\n .call(action, params, { isDebug: true });\n\n res.status(HttpStatus.OK).json({\n status_code: ErrorCodes.SUCCESS,\n data: {\n output: result,\n debug: {\n capabilityConfig: config,\n resolvedParams,\n duration: Date.now() - startTime,\n pluginID: config.pluginKey,\n action,\n },\n },\n } satisfies SuccessResponse<DebugExecuteResponseData>);\n } catch (error) {\n this.logger.error('Debug execution failed', error);\n res.status(HttpStatus.INTERNAL_SERVER_ERROR).json(\n this.buildErrorResponse(error),\n );\n }\n }\n\n /**\n * 构建错误响应\n */\n private buildErrorResponse(error: unknown): ErrorResponse {\n if (error instanceof CapabilityNotFoundError) {\n return {\n status_code: ErrorCodes.CAPABILITY_NOT_FOUND,\n error_msg: `Capability not found: ${error.capabilityId}`,\n };\n }\n if (error instanceof PluginNotFoundError) {\n return {\n status_code: ErrorCodes.PLUGIN_NOT_FOUND,\n error_msg: `Plugin not found, please install plugin: ${error.pluginKey}`,\n };\n }\n if (error instanceof ActionNotFoundError) {\n return {\n status_code: ErrorCodes.ACTION_NOT_FOUND,\n error_msg: `Action '${error.actionName}' not found in plugin ${error.pluginKey}`,\n };\n }\n return {\n status_code: ErrorCodes.EXECUTION_ERROR,\n error_msg: `Execution failed: ${error instanceof Error ? error.message : String(error)}`,\n };\n }\n\n @Post('debug/:capability_id/stream')\n async debugStream(\n @Param('capability_id') capabilityId: string,\n @Body() body: DebugRequestBody,\n @Res() res: Response,\n ): Promise<void> {\n const params = body.params ?? {};\n\n // 设置 SSE 响应头\n res.setHeader('Content-Type', 'text/event-stream');\n res.setHeader('Cache-Control', 'no-cache');\n res.setHeader('Connection', 'keep-alive');\n\n try {\n const config = this.getCapabilityConfig(capabilityId, body.capability);\n const action = await this.getActionName(config.pluginKey, body.action);\n const loggerContext = {\n capability_id: config.id,\n plugin_key: config.pluginKey,\n action,\n };\n\n // 打印入参\n this.logger.log(`Executing capability (stream)`, {\n ...loggerContext,\n input: stringifyForLog(params),\n });\n\n const capability = this.capabilityService.loadWithConfig(config);\n const eventStream = capability.callStreamWithEvents(action, params, { isDebug: true });\n\n // 延迟发送策略:缓存上一个 chunk,收到下一个 data 或 done 时再发送\n // 这样可以在最后一个 chunk 上标记 finished: true\n let pendingChunk: unknown | null = null;\n\n for await (const event of eventStream) {\n switch (event.type) {\n case 'data': {\n // 先发送上一个缓存的 chunk(如果有)\n if (pendingChunk !== null) {\n const response: StreamContentResponse = {\n status_code: ErrorCodes.SUCCESS,\n data: {\n type: 'content',\n delta: pendingChunk, // 直接透传 OutputSchema\n },\n };\n res.write(`data: ${JSON.stringify(response)}\\n\\n`);\n }\n // 缓存当前 chunk\n pendingChunk = event.data;\n break;\n }\n\n case 'done': {\n // 发送最后一个 chunk 并带上 finished: true\n if (pendingChunk !== null) {\n const response: StreamContentResponse = {\n status_code: ErrorCodes.SUCCESS,\n data: {\n type: 'content',\n delta: pendingChunk, // 直接透传 OutputSchema\n finished: true,\n },\n };\n res.write(`data: ${JSON.stringify(response)}\\n\\n`);\n }\n // 打印聚合结果(从 done 事件的 metadata 中获取)\n this.logger.log(`Capability (stream) executed successfully`, {\n ...loggerContext,\n duration_ms: event.metadata.duration,\n output: stringifyForLog(event.metadata.aggregated),\n });\n res.end();\n return;\n }\n\n case 'error': {\n // 错误事件:发送错误信息\n const response: StreamErrorResponse = {\n status_code: ErrorCodes.SUCCESS,\n data: {\n type: 'error',\n error: {\n code: 0,\n message: event.error.message,\n },\n },\n };\n res.write(`data: ${JSON.stringify(response)}\\n\\n`);\n res.end();\n return;\n }\n }\n }\n\n // 如果事件流正常结束但没有 done 事件,发送缓存的 chunk\n if (pendingChunk !== null) {\n const response: StreamContentResponse = {\n status_code: ErrorCodes.SUCCESS,\n data: {\n type: 'content',\n delta: pendingChunk, // 直接透传 OutputSchema\n finished: true,\n },\n };\n res.write(`data: ${JSON.stringify(response)}\\n\\n`);\n }\n res.end();\n } catch (error) {\n // 流异常中断(在事件流开始前的错误)\n const errorMsg = error instanceof Error ? error.message : String(error);\n const response: StreamErrorResponse = {\n status_code: ErrorCodes.SUCCESS,\n data: {\n type: 'error',\n error: {\n code: 0,\n message: errorMsg,\n },\n },\n };\n res.write(`data: ${JSON.stringify(response)}\\n\\n`);\n res.end();\n }\n }\n}\n","import {\n Controller,\n Get,\n Post,\n Param,\n Body,\n Res,\n HttpStatus,\n Logger,\n} from '@nestjs/common';\nimport { ApiExcludeController } from '@nestjs/swagger';\nimport type { Response } from 'express';\nimport {\n CapabilityService,\n CapabilityNotFoundError,\n ActionNotFoundError,\n} from '../services/capability.service';\nimport { PluginNotFoundError } from '../services/plugin-loader.service';\nimport type {\n SuccessResponse,\n ErrorResponse,\n ExecuteResponseData,\n ListResponseData,\n StreamContentResponse,\n StreamErrorResponse,\n} from '../interfaces';\nimport { ErrorCodes } from '../interfaces';\nimport { stringifyForLog } from '../utils';\n\ninterface ExecuteRequestBody {\n action: string;\n params: Record<string, unknown>;\n}\n@ApiExcludeController()\n@Controller('api/capability')\nexport class WebhookController {\n private readonly logger = new Logger(WebhookController.name);\n\n constructor(private readonly capabilityService: CapabilityService) {}\n\n @Get('list')\n list(@Res() res: Response): void {\n try {\n const capabilities = this.capabilityService.listCapabilities();\n res.status(HttpStatus.OK).json({\n status_code: ErrorCodes.SUCCESS,\n data: {\n capabilities: capabilities.map(c => ({\n id: c.id,\n name: c.name,\n pluginID: c.pluginKey,\n pluginVersion: c.pluginVersion,\n })),\n },\n } satisfies SuccessResponse<ListResponseData>);\n } catch (error) {\n this.logger.error('Failed to list capabilities', error);\n res.status(HttpStatus.INTERNAL_SERVER_ERROR).json({\n status_code: ErrorCodes.EXECUTION_ERROR,\n error_msg: `Failed to list capabilities: ${error instanceof Error ? error.message : String(error)}`,\n } satisfies ErrorResponse);\n }\n }\n\n @Post(':capability_id')\n async execute(\n @Param('capability_id') capabilityId: string,\n @Body() body: ExecuteRequestBody,\n @Res() res: Response,\n ): Promise<void> {\n try {\n const result = await this.capabilityService\n .load(capabilityId)\n .call(body.action, body.params);\n\n res.status(HttpStatus.OK).json({\n status_code: ErrorCodes.SUCCESS,\n data: {\n output: result,\n },\n } satisfies SuccessResponse<ExecuteResponseData>);\n } catch (error) {\n this.logger.error('Capability execution failed', error);\n res.status(HttpStatus.INTERNAL_SERVER_ERROR).json(\n this.buildErrorResponse(error),\n );\n }\n }\n\n /**\n * 构建错误响应\n */\n private buildErrorResponse(error: unknown): ErrorResponse {\n if (error instanceof CapabilityNotFoundError) {\n return {\n status_code: ErrorCodes.CAPABILITY_NOT_FOUND,\n error_msg: `Capability not found: ${error.capabilityId}`,\n };\n }\n if (error instanceof PluginNotFoundError) {\n return {\n status_code: ErrorCodes.PLUGIN_NOT_FOUND,\n error_msg: `Plugin not found, please install plugin: ${error.pluginKey}`,\n };\n }\n if (error instanceof ActionNotFoundError) {\n return {\n status_code: ErrorCodes.ACTION_NOT_FOUND,\n error_msg: `Action '${error.actionName}' not found in plugin ${error.pluginKey}`,\n };\n }\n return {\n status_code: ErrorCodes.EXECUTION_ERROR,\n error_msg: `Execution failed: ${error instanceof Error ? error.message : String(error)}`,\n };\n }\n\n @Post(':capability_id/stream')\n async executeStream(\n @Param('capability_id') capabilityId: string,\n @Body() body: ExecuteRequestBody,\n @Res() res: Response,\n ): Promise<void> {\n const loggerContext = {\n capability_id: capabilityId,\n action: body.action,\n };\n\n // 设置 SSE 响应头\n res.setHeader('Content-Type', 'text/event-stream');\n res.setHeader('Cache-Control', 'no-cache');\n res.setHeader('Connection', 'keep-alive');\n\n try {\n // 打印入参\n this.logger.log(`Executing capability (stream)`, {\n ...loggerContext,\n input: stringifyForLog(body.params),\n });\n\n const capability = this.capabilityService.load(capabilityId);\n const eventStream = capability.callStreamWithEvents(body.action, body.params);\n\n // 延迟发送策略:缓存上一个 chunk,收到下一个 data 或 done 时再发送\n // 这样可以在最后一个 chunk 上标记 finished: true\n let pendingChunk: unknown | null = null;\n\n for await (const event of eventStream) {\n switch (event.type) {\n case 'data': {\n // 先发送上一个缓存的 chunk(如果有)\n if (pendingChunk !== null) {\n const response: StreamContentResponse = {\n status_code: ErrorCodes.SUCCESS,\n data: {\n type: 'content',\n delta: pendingChunk, // 直接透传 OutputSchema\n },\n };\n res.write(`data: ${JSON.stringify(response)}\\n\\n`);\n }\n // 缓存当前 chunk\n pendingChunk = event.data;\n break;\n }\n\n case 'done': {\n // 发送最后一个 chunk 并带上 finished: true\n if (pendingChunk !== null) {\n const response: StreamContentResponse = {\n status_code: ErrorCodes.SUCCESS,\n data: {\n type: 'content',\n delta: pendingChunk, // 直接透传 OutputSchema\n finished: true,\n },\n };\n res.write(`data: ${JSON.stringify(response)}\\n\\n`);\n }\n // 打印聚合结果(从 done 事件的 metadata 中获取)\n this.logger.log(`Capability (stream) executed successfully`, {\n ...loggerContext,\n duration_ms: event.metadata.duration,\n output: stringifyForLog(event.metadata.aggregated),\n });\n res.end();\n return;\n }\n\n case 'error': {\n // 错误事件:发送错误信息\n const response: StreamErrorResponse = {\n status_code: ErrorCodes.SUCCESS,\n data: {\n type: 'error',\n error: {\n code: 0,\n message: event.error.message,\n },\n },\n };\n res.write(`data: ${JSON.stringify(response)}\\n\\n`);\n res.end();\n return;\n }\n }\n }\n\n // 如果事件流正常结束但没有 done 事件,发送缓存的 chunk\n if (pendingChunk !== null) {\n const response: StreamContentResponse = {\n status_code: ErrorCodes.SUCCESS,\n data: {\n type: 'content',\n delta: pendingChunk, // 直接透传 OutputSchema\n finished: true,\n },\n };\n res.write(`data: ${JSON.stringify(response)}\\n\\n`);\n }\n res.end();\n } catch (error) {\n // 流异常中断(在事件流开始前的错误)\n const errorMsg = error instanceof Error ? error.message : String(error);\n const response: StreamErrorResponse = {\n status_code: ErrorCodes.SUCCESS,\n data: {\n type: 'error',\n error: {\n code: 0,\n message: errorMsg,\n },\n },\n };\n res.write(`data: ${JSON.stringify(response)}\\n\\n`);\n res.end();\n }\n }\n}\n","import { Module, DynamicModule, Type } from '@nestjs/common';\nimport {\n CommonModule,\n RequestContextService,\n PLATFORM_HTTP_CLIENT,\n} from '@lark-apaas/nestjs-common';\nimport { DebugController, WebhookController } from './controllers';\nimport {\n CapabilityService,\n PluginLoaderService,\n TemplateEngineService,\n type CapabilityModuleOptions,\n} from './services';\n\nconst CAPABILITY_OPTIONS = Symbol('CAPABILITY_OPTIONS');\n\nconst isDevelopment = process.env.NODE_ENV === 'development';\n\nfunction getControllers(): Type[] {\n const controllers: Type[] = [WebhookController];\n if (isDevelopment) {\n controllers.push(DebugController);\n }\n return controllers;\n}\n\n@Module({\n imports: [CommonModule],\n controllers: getControllers(),\n providers: [CapabilityService, PluginLoaderService, TemplateEngineService],\n exports: [CapabilityService],\n})\nexport class CapabilityModule {\n static forRoot(options?: CapabilityModuleOptions): DynamicModule {\n return {\n module: CapabilityModule,\n imports: [CommonModule],\n controllers: getControllers(),\n providers: [\n {\n provide: CAPABILITY_OPTIONS,\n useValue: options ?? {},\n },\n {\n provide: CapabilityService,\n useFactory: (\n requestContextService: RequestContextService,\n httpClient: any,\n pluginLoader: PluginLoaderService,\n templateEngine: TemplateEngineService,\n moduleOptions: CapabilityModuleOptions,\n ) => {\n const service = new CapabilityService(\n requestContextService,\n httpClient,\n pluginLoader,\n templateEngine,\n );\n // 设置模块配置(包括 capabilitiesDir、enableWatching、watchDebounce)\n if (moduleOptions) {\n service.setOptions(moduleOptions);\n }\n return service;\n },\n inject: [\n RequestContextService,\n PLATFORM_HTTP_CLIENT,\n PluginLoaderService,\n TemplateEngineService,\n CAPABILITY_OPTIONS,\n ],\n },\n PluginLoaderService,\n TemplateEngineService,\n ],\n exports: [CapabilityService],\n };\n }\n}\n"],"mappings":"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","names":["getImportMetaUrl","document","URL","__filename","href","currentScript","tagName","toUpperCase","src","baseURI","importMetaUrl","ErrorCodes","SUCCESS","CAPABILITY_NOT_FOUND","PLUGIN_NOT_FOUND","ACTION_NOT_FOUND","PARAMS_VALIDATION_ERROR","EXECUTION_ERROR","TYPE_DEFAULT_VALUES","array","object","string","number","integer","boolean","null","TemplateEngineService","EXPR_REGEX","WHOLE_STRING_EXPR_REGEX","resolve","template","input","paramsSchema","resolveString","Array","isArray","map","item","resolveObject","wholeMatch","match","path","value","getValueByPath","undefined","defaultValue","getDefaultValueForPath","lastIndex","test","result","replace","String","key","Object","entries","obj","keys","split","current","schema","fieldSchema","getSchemaForPath","getDefaultValueFromSchema","currentSchema","properties","default","oneOf","anyOf","type","t","import_common","require","createRequire","PluginNotFoundError","Error","pluginKey","name","PluginLoadError","reason","PluginLoaderService","logger","Logger","pluginInstances","Map","pluginVersions","loadPlugin","cached","get","debug","log","resolvedPath","resolve","version","fileUrl","pathToFileURL","href","importPath","mod","pluginPackage","default","create","instance","set","error","code","message","String","isPluginInstalled","clearCache","delete","clearNodeModuleCache","Date","now","key","keys","clear","cache","clearModuleAndChildren","pluginScope","startsWith","split","slice","join","children","forEach","child","filename","includes","reloadPlugin","import_common","DEFAULT_MAX_LENGTH","truncateString","str","maxLength","length","slice","stringifyForLog","value","JSON","stringify","String","migrationAdaptor","promise","result","code","data","output","outcome","error","errorMessage","Error","message","String","appStatusCode","failureOutput","response","status","protocolStatusCode","body","JSON","stringify","stack","undefined","CapabilityNotFoundError","Error","capabilityId","name","ActionNotFoundError","pluginKey","actionName","CapabilityService","logger","Logger","capabilities","Map","filePathToId","capabilitiesDir","fileWatcher","options","requestContextService","platformHttpClient","pluginLoaderService","templateEngineService","isDev","process","env","NODE_ENV","join","cwd","setOptions","setCapabilitiesDir","dir","onModuleInit","loadCapabilities","enableWatching","startWatching","onModuleDestroy","stopWatching","existsSync","warn","pattern","debounce","watchDebounce","log","watch","persistent","ignoreInitial","awaitWriteFinish","stabilityThreshold","pollInterval","on","filePath","handleFileAdd","handleFileChange","handleFileUnlink","error","close","reloadAllCapabilities","clear","loadCapabilityFromFile","get","config","clearCache","reloadCapabilityFromFile","removeCapabilityByFile","content","readFileSync","JSON","parse","id","set","oldId","oldConfig","delete","files","readdirSync","filter","f","endsWith","file","size","listCapabilities","Array","from","values","getCapability","load","createExecutor","loadWithConfig","call","input","contextOverride","executeCall","callStream","executeCallStream","callStreamWithEvents","executeCallStreamWithEvents","isStream","checkIsStream","pluginInstance","loadPlugin","hasAction","isStreamAction","startTime","Date","now","loggerContext","capability_id","plugin_key","action","resolvedParams","formValue","resolve","paramsSchema","context","buildActionContext","is_stream","stringifyForLog","result","runStream","chunks","chunk","push","aggregate","run","duration_ms","output","message","String","aggregatedResult","runStreamWithEvents","event","type","data","metadata","aggregated","length","duration","code","override","userContext","getUserContext","isDebug","ctx","getContext","appId","userId","tenantId","import_common","DebugController","logger","Logger","name","capabilityService","pluginLoaderService","templateEngineService","list","res","capabilities","listCapabilities","status","HttpStatus","OK","json","status_code","ErrorCodes","SUCCESS","data","map","c","id","pluginID","pluginKey","pluginVersion","error","INTERNAL_SERVER_ERROR","EXECUTION_ERROR","error_msg","Error","message","String","getCapabilityConfig","capabilityId","bodyCapability","config","getCapability","CapabilityNotFoundError","getActionName","bodyAction","pluginInstance","loadPlugin","actions","listActions","length","debug","body","startTime","Date","now","params","capability","action","resolvedParams","resolve","formValue","paramsSchema","result","loadWithConfig","call","isDebug","output","capabilityConfig","duration","buildErrorResponse","CAPABILITY_NOT_FOUND","PluginNotFoundError","PLUGIN_NOT_FOUND","ActionNotFoundError","ACTION_NOT_FOUND","actionName","debugStream","setHeader","loggerContext","capability_id","plugin_key","log","input","stringifyForLog","eventStream","callStreamWithEvents","pendingChunk","event","type","response","delta","write","JSON","stringify","finished","duration_ms","metadata","aggregated","end","code","errorMsg","import_common","import_swagger","WebhookController","logger","Logger","name","capabilityService","list","res","capabilities","listCapabilities","status","HttpStatus","OK","json","status_code","ErrorCodes","SUCCESS","data","map","c","id","pluginID","pluginKey","pluginVersion","error","INTERNAL_SERVER_ERROR","EXECUTION_ERROR","error_msg","Error","message","String","execute","capabilityId","body","result","load","call","action","params","output","buildErrorResponse","CapabilityNotFoundError","CAPABILITY_NOT_FOUND","PluginNotFoundError","PLUGIN_NOT_FOUND","ActionNotFoundError","ACTION_NOT_FOUND","actionName","executeStream","loggerContext","capability_id","setHeader","log","input","stringifyForLog","capability","eventStream","callStreamWithEvents","pendingChunk","event","type","response","delta","write","JSON","stringify","finished","duration_ms","metadata","duration","aggregated","end","code","errorMsg","import_common","import_nestjs_common","CAPABILITY_OPTIONS","Symbol","isDevelopment","process","env","NODE_ENV","getControllers","controllers","WebhookController","push","DebugController","CapabilityModule","forRoot","options","module","imports","CommonModule","providers","provide","useValue","CapabilityService","useFactory","requestContextService","httpClient","pluginLoader","templateEngine","moduleOptions","service","setOptions","inject","RequestContextService","PLATFORM_HTTP_CLIENT","PluginLoaderService","TemplateEngineService","exports"]}
1
+ {"version":3,"sources":["../src/index.ts","../../../../node_modules/tsup/assets/cjs_shims.js","../src/interfaces/error-codes.ts","../src/services/template-engine.service.ts","../src/services/plugin-loader.service.ts","../src/services/capability.service.ts","../src/utils/log-utils.ts","../src/utils/migration-adaptor.ts","../src/controllers/debug.controller.ts","../src/controllers/webhook.controller.ts","../src/capability.module.ts"],"sourcesContent":["export * from './interfaces';\nexport * from './services';\nexport * from './controllers';\nexport * from './capability.module';\nexport { migrationAdaptor, type MigrationResponse, type FailureOutput } from './utils/migration-adaptor';\n","// Shim globals in cjs bundle\n// There's a weird bug that esbuild will always inject importMetaUrl\n// if we export it as `const importMetaUrl = ... __filename ...`\n// But using a function will not cause this issue\n\nconst getImportMetaUrl = () => \n typeof document === \"undefined\" \n ? new URL(`file:${__filename}`).href \n : (document.currentScript && document.currentScript.tagName.toUpperCase() === 'SCRIPT') \n ? document.currentScript.src \n : new URL(\"main.js\", document.baseURI).href;\n\nexport const importMetaUrl = /* @__PURE__ */ getImportMetaUrl()\n","/**\n * Capability 模块错误码\n */\nexport const ErrorCodes = {\n /** 成功 */\n SUCCESS: '0',\n /** 能力不存在 */\n CAPABILITY_NOT_FOUND: 'k_ec_cap_001',\n /** 插件不存在 */\n PLUGIN_NOT_FOUND: 'k_ec_cap_002',\n /** Action 不存在 */\n ACTION_NOT_FOUND: 'k_ec_cap_003',\n /** 参数验证失败 */\n PARAMS_VALIDATION_ERROR: 'k_ec_cap_004',\n /** 执行失败 */\n EXECUTION_ERROR: 'k_ec_cap_005',\n} as const;\n\nexport type ErrorCode = (typeof ErrorCodes)[keyof typeof ErrorCodes];\n","import { Injectable } from '@nestjs/common';\nimport { JSONSchema } from '../interfaces/capability-config.interface';\n\n/**\n * 类型到默认值的映射\n */\nconst TYPE_DEFAULT_VALUES: Record<string, unknown> = {\n array: [],\n object: {},\n string: '',\n number: 0,\n integer: 0,\n boolean: false,\n null: null,\n};\n\n/**\n * 模板引擎服务\n *\n * 支持语法:\n * - expr: '{{' + selector + '}}'\n * - selector: 'input.' + ident | selector.ident\n * - ident: [a-zA-Z_]([a-zA-Z_0-9])*\n *\n * 示例:\n * - {{input.a}}\n * - {{input.a.b}}\n * - \"this is {{input.a.b}}\"\n *\n * 求值规则:\n * - 如果整个字符串是单个表达式,保留原始类型\n * - 如果是字符串插值(多个表达式或混合内容),返回字符串\n * - 如果变量不存在:\n * - 未传 schema:返回原始表达式\n * - 传了 schema(整串表达式):按 schema 返回默认值\n * - 传了 schema(字符串插值):保留原始表达式\n */\n@Injectable()\nexport class TemplateEngineService {\n // 匹配 {{input.xxx}} 或 {{input.xxx.yyy}} 表达式\n private readonly EXPR_REGEX =\n /\\{\\{\\s*input\\.([a-zA-Z_][a-zA-Z_0-9]*(?:\\.[a-zA-Z_][a-zA-Z_0-9]*)*\\s*)\\}\\}/g;\n // 匹配整串单个表达式\n private readonly WHOLE_STRING_EXPR_REGEX =\n /^\\{\\{\\s*input\\.([a-zA-Z_][a-zA-Z_0-9]*(?:\\.[a-zA-Z_][a-zA-Z_0-9]*)*)\\s*\\}\\}$/;\n\n /**\n * 解析 formValue 模板\n * @param template - formValue 模板对象\n * @param input - 用户输入参数\n * @param paramsSchema - 可选,输入参数的 JSON Schema,用于推断默认值\n * @returns 解析后的参数对象\n */\n resolve(\n template: unknown,\n input: Record<string, unknown>,\n paramsSchema?: JSONSchema,\n ): unknown {\n if (typeof template === 'string') {\n return this.resolveString(template, input, paramsSchema);\n }\n\n if (Array.isArray(template)) {\n return template.map(item => this.resolve(item, input, paramsSchema));\n }\n\n if (template !== null && typeof template === 'object') {\n return this.resolveObject(\n template as Record<string, unknown>,\n input,\n paramsSchema,\n );\n }\n\n return template;\n }\n\n private resolveString(\n template: string,\n input: Record<string, unknown>,\n paramsSchema?: JSONSchema,\n ): unknown {\n // 情况1: 整串是单个表达式 → 保留原始类型\n const wholeMatch = template.match(this.WHOLE_STRING_EXPR_REGEX);\n if (wholeMatch) {\n const path = wholeMatch[1];\n const value = this.getValueByPath(input, path);\n\n // 变量存在,返回值\n if (value !== undefined) {\n return value;\n }\n\n // 变量不存在,尝试从 schema 获取默认值\n if (paramsSchema) {\n const defaultValue = this.getDefaultValueForPath(path, paramsSchema);\n if (defaultValue !== undefined) {\n return defaultValue;\n }\n }\n\n // 回退:返回原始表达式\n return template;\n }\n\n // 情况2: 字符串插值 → 返回字符串(不使用 schema 默认值)\n // 重置正则的 lastIndex(因为使用了 g 标志)\n this.EXPR_REGEX.lastIndex = 0;\n\n // 检查是否有任何表达式\n if (!this.EXPR_REGEX.test(template)) {\n return template;\n }\n\n // 重置并进行替换\n this.EXPR_REGEX.lastIndex = 0;\n const result = template.replace(this.EXPR_REGEX, (match, path) => {\n const value = this.getValueByPath(input, path);\n // 变量不存在,保留原始表达式\n if (value === undefined) {\n return match;\n }\n return String(value);\n });\n\n return result;\n }\n\n private resolveObject(\n template: Record<string, unknown>,\n input: Record<string, unknown>,\n paramsSchema?: JSONSchema,\n ): Record<string, unknown> {\n const result: Record<string, unknown> = {};\n\n for (const [key, value] of Object.entries(template)) {\n result[key] = this.resolve(value, input, paramsSchema);\n }\n\n return result;\n }\n\n private getValueByPath(obj: Record<string, unknown>, path: string): unknown {\n const keys = path.split('.');\n let current: unknown = obj;\n\n for (const key of keys) {\n if (current === null || current === undefined) {\n return undefined;\n }\n current = (current as Record<string, unknown>)[key];\n }\n\n return current;\n }\n\n /**\n * 根据路径从 schema 获取默认值\n * @param path - 变量路径,如 \"a.b.c\"\n * @param schema - JSON Schema\n * @returns 默认值,如果无法确定则返回 undefined\n */\n private getDefaultValueForPath(\n path: string,\n schema: JSONSchema,\n ): unknown {\n const fieldSchema = this.getSchemaForPath(path, schema);\n if (!fieldSchema) {\n return undefined;\n }\n return this.getDefaultValueFromSchema(fieldSchema);\n }\n\n /**\n * 根据路径查找对应的 schema 定义\n * @param path - 变量路径,如 \"a.b.c\"\n * @param schema - 根 JSON Schema\n * @returns 路径对应的 schema,如果不存在则返回 undefined\n */\n private getSchemaForPath(\n path: string,\n schema: JSONSchema,\n ): JSONSchema | undefined {\n const keys = path.split('.');\n let currentSchema: JSONSchema | undefined = schema;\n\n for (const key of keys) {\n if (!currentSchema?.properties) {\n return undefined;\n }\n currentSchema = currentSchema.properties[key];\n if (!currentSchema) {\n return undefined;\n }\n }\n\n return currentSchema;\n }\n\n /**\n * 从 schema 获取默认值\n * 优先级:default > type > undefined\n * @param schema - 字段的 JSON Schema\n * @returns 默认值\n */\n private getDefaultValueFromSchema(schema: JSONSchema): unknown {\n // 优先使用 schema 中定义的 default\n if (schema.default !== undefined) {\n return schema.default;\n }\n\n // 如果有 oneOf 或 anyOf,无法确定类型,返回 undefined\n if (schema.oneOf || schema.anyOf) {\n return undefined;\n }\n\n // 根据 type 返回默认值\n const type = schema.type;\n if (!type) {\n return undefined;\n }\n\n // 联合类型:取第一个类型的默认值\n if (Array.isArray(type)) {\n for (const t of type) {\n if (t in TYPE_DEFAULT_VALUES) {\n return TYPE_DEFAULT_VALUES[t];\n }\n }\n return undefined;\n }\n\n // 单一类型\n return TYPE_DEFAULT_VALUES[type];\n }\n}\n","import { Injectable, Logger } from '@nestjs/common';\nimport { createRequire } from 'node:module';\nimport { pathToFileURL } from 'node:url';\nimport * as fs from 'node:fs';\nimport * as path from 'node:path';\nimport type { PluginInstance, PluginPackage } from '../interfaces';\n\nexport interface PluginManifest {\n name: string;\n form?: {\n refType?: 'config' | 'action';\n schema?: unknown;\n };\n [key: string]: unknown;\n}\n\n// 社区标准模式:tsup shims 会为 CJS 输出自动处理 import.meta.url\nconst require = createRequire(import.meta.url);\n\nexport class PluginNotFoundError extends Error {\n readonly pluginKey: string;\n\n constructor(pluginKey: string) {\n super(`Plugin not found: ${pluginKey}`);\n this.name = 'PluginNotFoundError';\n this.pluginKey = pluginKey;\n }\n}\n\nexport class PluginLoadError extends Error {\n constructor(pluginKey: string, reason: string) {\n super(`Failed to load plugin ${pluginKey}: ${reason}`);\n this.name = 'PluginLoadError';\n }\n}\n\n@Injectable()\nexport class PluginLoaderService {\n private readonly logger = new Logger(PluginLoaderService.name);\n private readonly pluginInstances = new Map<string, PluginInstance>();\n /** 记录每个插件的加载版本(时间戳),用于 ESM 缓存绕过 */\n private readonly pluginVersions = new Map<string, number>();\n /** 缓存插件的 manifest.json */\n private readonly manifestCache = new Map<string, PluginManifest | null>();\n\n /**\n * 从入口路径向上查找包根目录(包含 package.json 的目录)\n */\n private findPackageRoot(entryPath: string): string | null {\n let dir = path.dirname(entryPath);\n while (dir !== path.dirname(dir)) {\n // 未到达根目录\n if (fs.existsSync(path.join(dir, 'package.json'))) {\n return dir;\n }\n dir = path.dirname(dir);\n }\n return null;\n }\n\n /**\n * 读取并缓存 manifest\n */\n getManifest(pluginKey: string): PluginManifest | null {\n // 检查缓存\n if (this.manifestCache.has(pluginKey)) {\n return this.manifestCache.get(pluginKey) ?? null;\n }\n\n try {\n // 1. 获取插件入口路径\n const entryPath = require.resolve(pluginKey);\n\n // 2. 从入口路径反推包根目录\n const packageRoot = this.findPackageRoot(entryPath);\n if (!packageRoot) {\n this.manifestCache.set(pluginKey, null);\n return null;\n }\n\n // 3. 读取 manifest.json\n const manifestPath = path.join(packageRoot, 'manifest.json');\n if (!fs.existsSync(manifestPath)) {\n this.manifestCache.set(pluginKey, null);\n return null;\n }\n\n const content = fs.readFileSync(manifestPath, 'utf-8');\n const manifest = JSON.parse(content) as PluginManifest;\n this.manifestCache.set(pluginKey, manifest);\n return manifest;\n } catch {\n // 读取失败时缓存 null,避免重复尝试\n this.manifestCache.set(pluginKey, null);\n return null;\n }\n }\n\n /**\n * 检查插件是否需要 config(form.refType === 'config')\n */\n isConfigRequired(pluginKey: string): boolean {\n const manifest = this.getManifest(pluginKey);\n return manifest?.form?.refType === 'config';\n }\n\n async loadPlugin(pluginKey: string, config?: unknown): Promise<PluginInstance> {\n const isConfigRequired = this.isConfigRequired(pluginKey);\n\n // refType: \"config\" 的插件不缓存,每次重新创建\n if (isConfigRequired) {\n return this.createPluginInstance(pluginKey, config);\n }\n\n // refType: \"action\" 的插件使用缓存\n const cached = this.pluginInstances.get(pluginKey);\n if (cached) {\n this.logger.debug(`Using cached plugin instance: ${pluginKey}`);\n return cached;\n }\n\n const instance = await this.createPluginInstance(pluginKey);\n this.pluginInstances.set(pluginKey, instance);\n return instance;\n }\n\n /**\n * 创建插件实例(内部方法)\n */\n private async createPluginInstance(pluginKey: string, config?: unknown): Promise<PluginInstance> {\n this.logger.log(`Loading plugin: ${pluginKey}${config !== undefined ? ' (with config)' : ''}`);\n\n try {\n // 1. 将包名解析为实际文件路径\n const resolvedPath = require.resolve(pluginKey);\n\n // 2. 使用 file:// URL + 时间戳绕过 ESM 缓存\n const version = this.pluginVersions.get(pluginKey) ?? 0;\n const fileUrl = pathToFileURL(resolvedPath).href;\n const importPath = version > 0 ? `${fileUrl}?v=${version}` : fileUrl;\n\n // 3. 动态导入(支持 ESM 和 CJS 插件)\n const mod = await import(importPath);\n const pluginPackage = (mod.default ?? mod) as PluginPackage;\n\n if (typeof pluginPackage.create !== 'function') {\n throw new PluginLoadError(pluginKey, 'Plugin does not export create() function');\n }\n\n const instance = pluginPackage.create(config);\n\n this.logger.log(`Plugin loaded successfully: ${pluginKey}`);\n return instance;\n } catch (error) {\n if ((error as NodeJS.ErrnoException).code === 'MODULE_NOT_FOUND') {\n throw new PluginNotFoundError(pluginKey);\n }\n throw new PluginLoadError(\n pluginKey,\n error instanceof Error ? error.message : String(error),\n );\n }\n }\n\n isPluginInstalled(pluginKey: string): boolean {\n try {\n require.resolve(pluginKey);\n return true;\n } catch {\n return false;\n }\n }\n\n /**\n * 清除插件缓存\n * - 清除应用层 pluginInstances 缓存\n * - 清除 manifest 缓存\n * - 清除 Node.js CJS 模块缓存(require.cache)\n * - 更新版本号,下次 import 时绕过 ESM 缓存\n * @param pluginKey - 插件标识,不传则清除所有\n */\n clearCache(pluginKey?: string): void {\n if (pluginKey) {\n this.pluginInstances.delete(pluginKey);\n this.manifestCache.delete(pluginKey);\n this.clearNodeModuleCache(pluginKey);\n // 更新版本号,下次 import 时使用新的 URL 绕过 ESM 缓存\n this.pluginVersions.set(pluginKey, Date.now());\n this.logger.log(`Cleared cache for plugin: ${pluginKey}`);\n } else {\n // 清除所有插件\n for (const key of this.pluginInstances.keys()) {\n this.clearNodeModuleCache(key);\n this.pluginVersions.set(key, Date.now());\n }\n this.pluginInstances.clear();\n this.manifestCache.clear();\n this.logger.log('Cleared all plugin caches');\n }\n }\n\n /**\n * 清除 CJS 模块缓存\n */\n private clearNodeModuleCache(pluginKey: string): void {\n try {\n const resolvedPath = require.resolve(pluginKey);\n\n // 清除主模块及其依赖\n const mod = require.cache[resolvedPath];\n if (mod) {\n this.clearModuleAndChildren(mod, pluginKey);\n }\n\n delete require.cache[resolvedPath];\n } catch {\n // 插件可能未安装或是纯 ESM 模块,忽略错误\n }\n }\n\n /**\n * 递归清除子模块缓存\n */\n private clearModuleAndChildren(mod: NodeModule, pluginKey: string): void {\n // 只清除插件目录下的模块,避免清除核心模块\n const pluginScope = pluginKey.startsWith('@') ? pluginKey.split('/').slice(0, 2).join('/') : pluginKey;\n\n mod.children.forEach(child => {\n if (child.filename.includes(`node_modules/${pluginScope}`)) {\n this.clearModuleAndChildren(child, pluginKey);\n delete require.cache[child.filename];\n }\n });\n }\n\n /**\n * 强制重新加载插件\n * @param pluginKey - 插件标识\n */\n async reloadPlugin(pluginKey: string): Promise<PluginInstance> {\n this.clearCache(pluginKey);\n return this.loadPlugin(pluginKey);\n }\n}\n","import { Injectable, Logger, Inject, OnModuleInit, OnModuleDestroy } from '@nestjs/common';\nimport {\n RequestContextService,\n PLATFORM_HTTP_CLIENT,\n type PlatformHttpClient,\n} from '@lark-apaas/nestjs-common';\nimport * as fs from 'node:fs';\nimport * as path from 'node:path';\nimport * as chokidar from 'chokidar';\nimport type {\n CapabilityConfig,\n PluginActionContext,\n PluginInstance,\n UserContext,\n StreamEvent,\n} from '../interfaces';\nimport { PluginLoaderService } from './plugin-loader.service';\nimport { TemplateEngineService } from './template-engine.service';\nimport { stringifyForLog } from '../utils';\n\nexport class CapabilityNotFoundError extends Error {\n readonly capabilityId: string;\n\n constructor(capabilityId: string) {\n super(`Capability not found: ${capabilityId}`);\n this.name = 'CapabilityNotFoundError';\n this.capabilityId = capabilityId;\n }\n}\n\nexport class ActionNotFoundError extends Error {\n readonly pluginKey: string;\n readonly actionName: string;\n\n constructor(pluginKey: string, actionName: string) {\n super(`Action '${actionName}' not found in plugin ${pluginKey}`);\n this.name = 'ActionNotFoundError';\n this.pluginKey = pluginKey;\n this.actionName = actionName;\n }\n}\n\nexport interface CapabilityExecutor {\n /**\n * 调用 capability(始终返回 Promise)\n * - unary action: 直接返回结果\n * - stream action: 内部聚合所有 chunk 后返回\n */\n call(actionName: string, input: unknown, context?: Partial<PluginActionContext>): Promise<unknown>;\n\n /**\n * 流式调用 capability,返回原始流\n * - 返回原始 AsyncIterable\n * - 如果 action 是 unary,包装为单次 yield\n */\n callStream(actionName: string, input: unknown, context?: Partial<PluginActionContext>): AsyncIterable<unknown>;\n\n /**\n * 流式调用 capability,返回带事件协议的流(推荐)\n * - 返回 StreamEvent 类型的 AsyncIterable\n * - 支持 data/done/error 三种事件类型\n * - Controller 层应优先使用此方法实现边收边发\n */\n callStreamWithEvents(\n actionName: string,\n input: unknown,\n context?: Partial<PluginActionContext>,\n ): AsyncIterable<StreamEvent<unknown>>;\n\n /**\n * 检查 action 是否为流式\n */\n isStream(actionName: string): Promise<boolean>;\n}\n\nexport interface CapabilityModuleOptions {\n /** 能力配置目录路径,默认 server/capabilities */\n capabilitiesDir?: string;\n /** 是否启用文件监听(热更新),默认 false */\n enableWatching?: boolean;\n /** 文件变更防抖时间(ms),默认 300 */\n watchDebounce?: number;\n}\n\n@Injectable()\nexport class CapabilityService implements OnModuleInit, OnModuleDestroy {\n private readonly logger = new Logger(CapabilityService.name);\n private readonly capabilities = new Map<string, CapabilityConfig>();\n /** 文件路径到 capability id 的映射,用于文件删除时查找 */\n private readonly filePathToId = new Map<string, string>();\n private capabilitiesDir: string;\n private fileWatcher: chokidar.FSWatcher | null = null;\n private options: CapabilityModuleOptions = {};\n\n constructor(\n private readonly requestContextService: RequestContextService,\n @Inject(PLATFORM_HTTP_CLIENT) private readonly platformHttpClient: PlatformHttpClient,\n private readonly pluginLoaderService: PluginLoaderService,\n private readonly templateEngineService: TemplateEngineService,\n ) {\n const isDev = process.env.NODE_ENV === 'development';\n this.capabilitiesDir = path.join(process.cwd(), isDev ? 'server/capabilities' : 'capabilities');\n }\n\n /**\n * 设置模块配置\n */\n setOptions(options: CapabilityModuleOptions): void {\n this.options = options;\n if (options.capabilitiesDir) {\n this.capabilitiesDir = options.capabilitiesDir;\n }\n }\n\n setCapabilitiesDir(dir: string): void {\n this.capabilitiesDir = dir;\n }\n\n async onModuleInit(): Promise<void> {\n await this.loadCapabilities();\n\n // 如果启用文件监听,开始监听\n if (this.options.enableWatching) {\n this.startWatching();\n }\n }\n\n async onModuleDestroy(): Promise<void> {\n this.stopWatching();\n }\n\n // ==================== 文件监听相关方法 ====================\n\n /**\n * 启动文件监听(沙箱环境自动调用)\n */\n startWatching(): void {\n if (this.fileWatcher) {\n return; // 已在监听\n }\n\n if (!fs.existsSync(this.capabilitiesDir)) {\n this.logger.warn(`Cannot start watching: directory not found: ${this.capabilitiesDir}`);\n return;\n }\n\n const pattern = path.join(this.capabilitiesDir, '*.json');\n const debounce = this.options.watchDebounce ?? 300;\n\n this.logger.log(`Starting file watcher: ${pattern}`);\n\n this.fileWatcher = chokidar.watch(pattern, {\n persistent: true,\n ignoreInitial: true, // 忽略初始扫描\n awaitWriteFinish: {\n // 等待写入完成\n stabilityThreshold: debounce,\n pollInterval: 100,\n },\n });\n\n this.fileWatcher\n .on('add', filePath => this.handleFileAdd(filePath))\n .on('change', filePath => this.handleFileChange(filePath))\n .on('unlink', filePath => this.handleFileUnlink(filePath))\n .on('error', error => this.logger.error('File watcher error:', error));\n\n this.logger.log('File watcher started');\n }\n\n /**\n * 停止文件监听\n */\n stopWatching(): void {\n if (this.fileWatcher) {\n this.fileWatcher.close();\n this.fileWatcher = null;\n this.logger.log('File watcher stopped');\n }\n }\n\n /**\n * 重新加载所有能力配置\n */\n async reloadAllCapabilities(): Promise<void> {\n this.capabilities.clear();\n this.filePathToId.clear();\n await this.loadCapabilities();\n }\n\n private handleFileAdd(filePath: string): void {\n this.logger.log(`Capability file added: ${filePath}`);\n try {\n this.loadCapabilityFromFile(filePath);\n\n // 清除关联插件的缓存,确保加载最新版本\n const capabilityId = this.filePathToId.get(filePath);\n if (capabilityId) {\n const config = this.capabilities.get(capabilityId);\n if (config?.pluginKey) {\n this.pluginLoaderService.clearCache(config.pluginKey);\n this.logger.log(`Cleared plugin cache for new capability: ${config.pluginKey}`);\n }\n }\n } catch (error) {\n this.logger.error(`Failed to load new capability: ${filePath}`, error);\n }\n }\n\n private handleFileChange(filePath: string): void {\n this.logger.log(`Capability file changed: ${filePath}`);\n try {\n this.reloadCapabilityFromFile(filePath);\n } catch (error) {\n this.logger.error(`Failed to reload capability: ${filePath}`, error);\n }\n }\n\n private handleFileUnlink(filePath: string): void {\n this.logger.log(`Capability file removed: ${filePath}`);\n this.removeCapabilityByFile(filePath);\n }\n\n /**\n * 从文件加载单个能力配置\n */\n private loadCapabilityFromFile(filePath: string): void {\n const content = fs.readFileSync(filePath, 'utf-8');\n const config = JSON.parse(content) as CapabilityConfig;\n\n if (!config.id) {\n this.logger.warn(`Skipping capability without id: ${filePath}`);\n return;\n }\n\n this.capabilities.set(config.id, config);\n this.filePathToId.set(filePath, config.id);\n this.logger.log(`Loaded capability: ${config.id} (${config.name})`);\n }\n\n /**\n * 重新加载单个能力配置\n */\n private reloadCapabilityFromFile(filePath: string): void {\n // 先移除旧配置(如果存在)\n const oldId = this.filePathToId.get(filePath);\n if (oldId) {\n const oldConfig = this.capabilities.get(oldId);\n if (oldConfig) {\n // 清除关联插件的缓存\n this.pluginLoaderService.clearCache(oldConfig.pluginKey);\n }\n this.capabilities.delete(oldId);\n }\n\n // 重新加载\n this.loadCapabilityFromFile(filePath);\n }\n\n /**\n * 根据文件路径移除能力配置\n */\n private removeCapabilityByFile(filePath: string): void {\n const capabilityId = this.filePathToId.get(filePath);\n if (capabilityId) {\n this.capabilities.delete(capabilityId);\n this.filePathToId.delete(filePath);\n this.logger.log(`Removed capability: ${capabilityId}`);\n }\n }\n\n // ==================== 原有方法 ====================\n\n private async loadCapabilities(): Promise<void> {\n this.logger.log(`Loading capabilities from ${this.capabilitiesDir}`);\n\n if (!fs.existsSync(this.capabilitiesDir)) {\n this.logger.warn(`Capabilities directory not found: ${this.capabilitiesDir}`);\n return;\n }\n\n const files = fs.readdirSync(this.capabilitiesDir).filter(f => f.endsWith('.json'));\n\n for (const file of files) {\n try {\n const filePath = path.join(this.capabilitiesDir, file);\n this.loadCapabilityFromFile(filePath);\n } catch (error) {\n this.logger.error(`Failed to load capability from ${file}:`, error);\n }\n }\n\n this.logger.log(`Loaded ${this.capabilities.size} capabilities`);\n }\n\n listCapabilities(): CapabilityConfig[] {\n return Array.from(this.capabilities.values());\n }\n\n getCapability(capabilityId: string): CapabilityConfig | null {\n return this.capabilities.get(capabilityId) ?? null;\n }\n\n load(capabilityId: string): CapabilityExecutor {\n const config = this.capabilities.get(capabilityId);\n if (!config) {\n throw new CapabilityNotFoundError(capabilityId);\n }\n\n return this.createExecutor(config);\n }\n\n /**\n * 使用传入的配置加载能力执行器\n * 用于 debug 场景,支持用户传入自定义配置\n */\n loadWithConfig(config: CapabilityConfig): CapabilityExecutor {\n return this.createExecutor(config);\n }\n\n private createExecutor(config: CapabilityConfig): CapabilityExecutor {\n return {\n call: async (\n actionName: string,\n input: unknown,\n contextOverride?: Partial<PluginActionContext>,\n ) => {\n return this.executeCall(config, actionName, input, contextOverride);\n },\n\n callStream: (\n actionName: string,\n input: unknown,\n contextOverride?: Partial<PluginActionContext>,\n ) => {\n return this.executeCallStream(config, actionName, input, contextOverride);\n },\n\n callStreamWithEvents: (\n actionName: string,\n input: unknown,\n contextOverride?: Partial<PluginActionContext>,\n ) => {\n return this.executeCallStreamWithEvents(config, actionName, input, contextOverride);\n },\n\n isStream: async (actionName: string) => {\n return this.checkIsStream(config, actionName);\n },\n };\n }\n\n /**\n * 加载插件实例并解析参数\n * 根据 manifest 的 form.refType 决定 formValue 的消费方式\n */\n private async loadPluginAndResolveParams(\n config: CapabilityConfig,\n input: unknown,\n ): Promise<{ pluginInstance: PluginInstance; resolvedParams: unknown }> {\n const isConfigRequired = this.pluginLoaderService.isConfigRequired(config.pluginKey);\n\n if (isConfigRequired) {\n // refType: \"config\" - formValue 作为 config 传给 create()\n const pluginInstance = await this.pluginLoaderService.loadPlugin(\n config.pluginKey,\n config.formValue,\n );\n // 直接使用 input 作为 action 参数(不经过模板引擎)\n return { pluginInstance, resolvedParams: input };\n }\n\n // refType: \"action\" 或默认 - 保持现有行为\n const pluginInstance = await this.pluginLoaderService.loadPlugin(config.pluginKey);\n const resolvedParams = config.formValue\n ? this.templateEngineService.resolve(config.formValue, input as Record<string, unknown>, config.paramsSchema)\n : input;\n return { pluginInstance, resolvedParams };\n }\n\n /**\n * 检查 action 是否为流式\n */\n private async checkIsStream(config: CapabilityConfig, actionName: string): Promise<boolean> {\n const { pluginInstance } = await this.loadPluginAndResolveParams(config, {});\n\n if (!pluginInstance.hasAction(actionName)) {\n throw new ActionNotFoundError(config.pluginKey, actionName);\n }\n\n return pluginInstance.isStreamAction?.(actionName) ?? false;\n }\n\n /**\n * 执行 capability(始终返回 Promise)\n * - unary action: 直接返回结果\n * - stream action: 内部聚合所有 chunk 后返回\n */\n private async executeCall(\n config: CapabilityConfig,\n actionName: string,\n input: unknown,\n contextOverride?: Partial<PluginActionContext>,\n ): Promise<unknown> {\n const startTime = Date.now();\n const loggerContext = {\n capability_id: config.id,\n plugin_key: config.pluginKey,\n action: actionName,\n };\n try {\n const { pluginInstance, resolvedParams } = await this.loadPluginAndResolveParams(config, input);\n\n if (!pluginInstance.hasAction(actionName)) {\n throw new ActionNotFoundError(config.pluginKey, actionName);\n }\n\n const context = this.buildActionContext(contextOverride);\n const isStream = pluginInstance.isStreamAction?.(actionName) ?? false;\n\n // 打印入参\n this.logger.log('Executing capability (call)', {\n ...loggerContext,\n is_stream: isStream,\n input: stringifyForLog(input),\n });\n\n let result: unknown;\n\n if (isStream && pluginInstance.runStream) {\n // 流式 action:聚合所有 chunk\n const chunks: unknown[] = [];\n for await (const chunk of pluginInstance.runStream(actionName, context, resolvedParams)) {\n chunks.push(chunk);\n }\n // 使用插件的聚合方法,或默认返回 chunks 数组\n result = pluginInstance.aggregate\n ? pluginInstance.aggregate(actionName, chunks)\n : chunks;\n } else {\n // 非流式 action:直接调用\n result = await pluginInstance.run(actionName, context, resolvedParams);\n }\n\n // 打印返回值\n this.logger.log('Capability (call) executed successfully', {\n ...loggerContext,\n duration_ms: Date.now() - startTime,\n output: stringifyForLog(result),\n });\n\n return result;\n } catch (error) {\n this.logger.error('Capability (call) execution failed', {\n ...loggerContext,\n duration_ms: Date.now() - startTime,\n error: error instanceof Error ? error.message : String(error),\n });\n throw error;\n }\n }\n\n /**\n * 流式执行 capability\n * - stream action: 返回原始 AsyncIterable\n * - unary action: 包装为单次 yield\n */\n private async *executeCallStream(\n config: CapabilityConfig,\n actionName: string,\n input: unknown,\n contextOverride?: Partial<PluginActionContext>,\n ): AsyncIterable<unknown> {\n const startTime = Date.now();\n const loggerContext = {\n capability_id: config.id,\n plugin_key: config.pluginKey,\n action: actionName,\n };\n const chunks: unknown[] = [];\n try {\n const { pluginInstance, resolvedParams } = await this.loadPluginAndResolveParams(config, input);\n\n if (!pluginInstance.hasAction(actionName)) {\n throw new ActionNotFoundError(config.pluginKey, actionName);\n }\n\n const context = this.buildActionContext(contextOverride);\n const isStream = pluginInstance.isStreamAction?.(actionName) ?? false;\n\n // 打印入参\n this.logger.log('Executing capability (stream)', {\n ...loggerContext,\n is_stream: isStream,\n input: stringifyForLog(input),\n });\n\n if (isStream && pluginInstance.runStream) {\n // 流式 action:透传 AsyncIterable,同时收集 chunks\n for await (const chunk of pluginInstance.runStream(actionName, context, resolvedParams)) {\n chunks.push(chunk);\n yield chunk;\n }\n } else {\n // 非流式 action:包装为单次 yield\n const result = await pluginInstance.run(actionName, context, resolvedParams);\n chunks.push(result);\n yield result;\n }\n\n // 打印聚合结果\n const aggregatedResult = pluginInstance.aggregate\n ? pluginInstance.aggregate(actionName, chunks)\n : chunks;\n this.logger.log('Capability (stream) executed successfully', {\n ...loggerContext,\n duration_ms: Date.now() - startTime,\n output: stringifyForLog(aggregatedResult),\n });\n } catch (error) {\n this.logger.error('Capability (stream) execution failed', {\n ...loggerContext,\n duration_ms: Date.now() - startTime,\n error: error instanceof Error ? error.message : String(error),\n });\n throw error;\n }\n }\n\n /**\n * 流式执行 capability,返回带事件协议的流\n * - 优先使用 pluginInstance.runStreamWithEvents\n * - 如果插件不支持,则包装 runStream/run 为 StreamEvent\n */\n private async *executeCallStreamWithEvents(\n config: CapabilityConfig,\n actionName: string,\n input: unknown,\n contextOverride?: Partial<PluginActionContext>,\n ): AsyncIterable<StreamEvent<unknown>> {\n const startTime = Date.now();\n const loggerContext = {\n capability_id: config.id,\n plugin_key: config.pluginKey,\n action: actionName,\n };\n const chunks: unknown[] = [];\n try {\n const { pluginInstance, resolvedParams } = await this.loadPluginAndResolveParams(config, input);\n\n if (!pluginInstance.hasAction(actionName)) {\n throw new ActionNotFoundError(config.pluginKey, actionName);\n }\n\n const context = this.buildActionContext(contextOverride);\n const isStream = pluginInstance.isStreamAction?.(actionName) ?? false;\n\n // 打印入参\n this.logger.log('Executing capability (streamWithEvents)', {\n ...loggerContext,\n is_stream: isStream,\n input: stringifyForLog(input),\n });\n\n // 优先使用 runStreamWithEvents\n if (pluginInstance.runStreamWithEvents) {\n for await (const event of pluginInstance.runStreamWithEvents(actionName, context, resolvedParams)) {\n // 收集 data 事件的数据用于聚合\n if (event.type === 'data') {\n chunks.push(event.data);\n yield event;\n } else if (event.type === 'done') {\n // 拦截 done 事件,添加聚合结果\n const aggregatedResult = pluginInstance.aggregate\n ? pluginInstance.aggregate(actionName, chunks)\n : chunks;\n yield {\n type: 'done',\n metadata: {\n ...event.metadata,\n aggregated: aggregatedResult,\n },\n };\n } else {\n yield event;\n }\n }\n } else {\n // 回退:包装 runStream 或 run 为 StreamEvent\n if (isStream && pluginInstance.runStream) {\n // 流式 action:包装每个 chunk 为 data 事件\n for await (const chunk of pluginInstance.runStream(actionName, context, resolvedParams)) {\n chunks.push(chunk);\n yield { type: 'data', data: chunk };\n }\n } else {\n // 非流式 action:包装结果为单个 data 事件\n const result = await pluginInstance.run(actionName, context, resolvedParams);\n chunks.push(result);\n yield { type: 'data', data: result };\n }\n\n // 聚合结果\n const aggregatedResult = pluginInstance.aggregate\n ? pluginInstance.aggregate(actionName, chunks)\n : chunks;\n\n // 发送 done 事件(包含聚合结果)\n yield {\n type: 'done',\n metadata: {\n chunks: chunks.length,\n duration: Date.now() - startTime,\n aggregated: aggregatedResult,\n },\n };\n }\n\n // 打印聚合结果\n const aggregatedResult = pluginInstance.aggregate\n ? pluginInstance.aggregate(actionName, chunks)\n : chunks;\n this.logger.log('Capability (streamWithEvents) executed successfully', {\n ...loggerContext,\n duration_ms: Date.now() - startTime,\n output: stringifyForLog(aggregatedResult),\n });\n } catch (error) {\n this.logger.error('Capability (streamWithEvents) execution failed', {\n ...loggerContext,\n duration_ms: Date.now() - startTime,\n error: error instanceof Error ? error.message : String(error),\n });\n\n // 发送 error 事件\n yield {\n type: 'error',\n error: {\n code: 'EXECUTION_ERROR',\n message: error instanceof Error ? error.message : String(error),\n },\n };\n }\n }\n\n private buildActionContext(override?: Partial<PluginActionContext>): PluginActionContext {\n return {\n logger: this.logger,\n platformHttpClient: this.platformHttpClient,\n userContext: override?.userContext ?? this.getUserContext(),\n isDebug: override?.isDebug ?? false,\n };\n }\n\n private getUserContext(): UserContext {\n const ctx = this.requestContextService.getContext();\n return {\n appId: ctx?.appId ?? '',\n userId: ctx?.userId ?? '',\n tenantId: ctx?.tenantId ?? '',\n };\n }\n}\n","/**\n * 日志工具函数\n */\n\nconst DEFAULT_MAX_LENGTH = 1000;\n\n/**\n * 截断字符串\n */\nfunction truncateString(str: string, maxLength: number): string {\n if (str.length <= maxLength) {\n return str;\n }\n return str.slice(0, maxLength) + `... [truncated, total ${str.length} chars]`;\n}\n\n/**\n * 将对象转换为可打印的字符串,超过指定长度时截断\n * @param value - 要打印的值\n * @param maxLength - 最大字符数,默认 1000\n */\nexport function stringifyForLog(value: unknown, maxLength: number = DEFAULT_MAX_LENGTH): string {\n try {\n const str = JSON.stringify(value, null, 2);\n return truncateString(str, maxLength);\n } catch {\n // 处理循环引用等情况\n const str = String(value);\n return truncateString(str, maxLength);\n }\n}\n","/**\n * Migration Adaptor\n *\n * 用于迁移老版本 capability 调用代码,将新版本的返回结果包装为老版本的 Response 结构\n */\n\n/**\n * 失败输出结构\n */\nexport interface FailureOutput {\n response: {\n status: {\n /** 协议层返回码(如 HTTP 状态码) */\n protocolStatusCode: string;\n /** 业务应用状态码 */\n appStatusCode: string;\n };\n /** 返回体(JSON 字符串形式) */\n body: string;\n };\n}\n\n/**\n * 老版本 capability 调用的 Response 结构\n *\n * 注意:output 使用 any 类型以简化迁移代码,避免类型断言\n */\nexport interface MigrationResponse {\n /** 当 code=0 时表示执行成功,当 code!=0 时表示执行失败 */\n code: number;\n data?: {\n /** 执行成功时的输出结果(any 类型,兼容迁移场景) */\n output?: any;\n /** 执行结果,'success' 或 'error' */\n outcome?: 'success' | 'error';\n /** 执行失败时的输出结果 */\n failureOutput?: FailureOutput;\n };\n /** 发生错误时的错误信息 */\n message?: string;\n}\n\n/**\n * 迁移适配器\n *\n * 将 CapabilityService.load().call() 的返回结果包装为老版本的 Response 结构\n *\n * @example\n * // 老代码\n * const result = await generateTaskDescription(params);\n *\n * // 新代码\n * const result = await migrationAdaptor(\n * this.capabilityService.load('ai_generate_task_description').call('run', params)\n * );\n *\n * // result 结构\n * // {\n * // code: 0,\n * // data: {\n * // output: { ... }, // 能力执行结果\n * // outcome: 'success'\n * // }\n * // }\n */\nexport async function migrationAdaptor(\n promise: Promise<unknown>,\n): Promise<MigrationResponse> {\n try {\n const result = await promise;\n\n return {\n code: 0,\n data: {\n output: result,\n outcome: 'success',\n },\n };\n } catch (error) {\n const errorMessage = error instanceof Error ? error.message : String(error);\n const appStatusCode = error instanceof Error && 'code' in error ? String(error.code) : 'EXECUTION_ERROR';\n\n return {\n code: -1,\n data: {\n outcome: 'error',\n failureOutput: {\n response: {\n status: {\n protocolStatusCode: '500',\n appStatusCode,\n },\n body: JSON.stringify({\n error: errorMessage,\n stack: error instanceof Error ? error.stack : undefined,\n }),\n },\n },\n },\n message: errorMessage,\n };\n }\n}\n","import {\n Controller,\n Post,\n Get,\n Param,\n Body,\n Res,\n HttpStatus,\n Logger,\n} from '@nestjs/common';\nimport { ApiExcludeController } from '@nestjs/swagger';\nimport type { Response } from 'express';\nimport {\n CapabilityService,\n CapabilityNotFoundError,\n ActionNotFoundError,\n} from '../services/capability.service';\nimport { PluginLoaderService, PluginNotFoundError } from '../services/plugin-loader.service';\nimport { TemplateEngineService } from '../services/template-engine.service';\nimport type {\n CapabilityConfig,\n SuccessResponse,\n ErrorResponse,\n DebugExecuteResponseData,\n ListResponseData,\n StreamContentResponse,\n StreamErrorResponse,\n} from '../interfaces';\nimport { ErrorCodes } from '../interfaces';\nimport { stringifyForLog } from '../utils';\n\ninterface DebugRequestBody {\n action?: string;\n params?: Record<string, unknown>;\n capability?: CapabilityConfig;\n}\n\n@ApiExcludeController()\n@Controller('__innerapi__/capability')\nexport class DebugController {\n private readonly logger = new Logger(DebugController.name);\n\n constructor(\n private readonly capabilityService: CapabilityService,\n private readonly pluginLoaderService: PluginLoaderService,\n private readonly templateEngineService: TemplateEngineService,\n ) {}\n\n @Get('list')\n list(@Res() res: Response): void {\n try {\n const capabilities = this.capabilityService.listCapabilities();\n\n res.status(HttpStatus.OK).json({\n status_code: ErrorCodes.SUCCESS,\n data: {\n capabilities: capabilities.map(c => ({\n id: c.id,\n name: c.name,\n pluginID: c.pluginKey,\n pluginVersion: c.pluginVersion,\n })),\n },\n } satisfies SuccessResponse<ListResponseData>);\n } catch (error) {\n this.logger.error('Failed to list capabilities', error);\n res.status(HttpStatus.INTERNAL_SERVER_ERROR).json({\n status_code: ErrorCodes.EXECUTION_ERROR,\n error_msg: `Failed to list capabilities: ${error instanceof Error ? error.message : String(error)}`,\n } satisfies ErrorResponse);\n }\n }\n\n /**\n * 获取 capability 配置\n * 优先使用 body.capability,否则从服务获取\n */\n private getCapabilityConfig(\n capabilityId: string,\n bodyCapability?: CapabilityConfig,\n ): CapabilityConfig {\n if (bodyCapability) {\n return bodyCapability;\n }\n\n const config = this.capabilityService.getCapability(capabilityId);\n if (!config) {\n throw new CapabilityNotFoundError(capabilityId);\n }\n\n return config;\n }\n\n /**\n * 获取 action 名称\n * 优先使用传入的 action,否则使用插件第一个 action\n */\n private async getActionName(pluginKey: string, formValue?: unknown, bodyAction?: string): Promise<string> {\n if (bodyAction) {\n return bodyAction;\n }\n\n // 根据 manifest 决定是否传入 config\n const isConfigRequired = this.pluginLoaderService.isConfigRequired(pluginKey);\n const pluginInstance = isConfigRequired\n ? await this.pluginLoaderService.loadPlugin(pluginKey, formValue)\n : await this.pluginLoaderService.loadPlugin(pluginKey);\n\n const actions = pluginInstance.listActions();\n\n if (actions.length === 0) {\n throw new Error(`Plugin ${pluginKey} has no actions`);\n }\n\n return actions[0];\n }\n\n @Post('debug/:capability_id')\n async debug(\n @Param('capability_id') capabilityId: string,\n @Body() body: DebugRequestBody,\n @Res() res: Response,\n ): Promise<void> {\n const startTime = Date.now();\n const params = body.params ?? {};\n\n try {\n const config = this.getCapabilityConfig(capabilityId, body.capability);\n const action = await this.getActionName(config.pluginKey, config.formValue, body.action);\n\n const resolvedParams = this.templateEngineService.resolve(\n config.formValue,\n params,\n config.paramsSchema,\n );\n\n const result = await this.capabilityService\n .loadWithConfig(config)\n .call(action, params, { isDebug: true });\n\n res.status(HttpStatus.OK).json({\n status_code: ErrorCodes.SUCCESS,\n data: {\n output: result,\n debug: {\n capabilityConfig: config,\n resolvedParams,\n duration: Date.now() - startTime,\n pluginID: config.pluginKey,\n action,\n },\n },\n } satisfies SuccessResponse<DebugExecuteResponseData>);\n } catch (error) {\n this.logger.error('Debug execution failed', error);\n res.status(HttpStatus.INTERNAL_SERVER_ERROR).json(\n this.buildErrorResponse(error),\n );\n }\n }\n\n /**\n * 构建错误响应\n */\n private buildErrorResponse(error: unknown): ErrorResponse {\n if (error instanceof CapabilityNotFoundError) {\n return {\n status_code: ErrorCodes.CAPABILITY_NOT_FOUND,\n error_msg: `Capability not found: ${error.capabilityId}`,\n };\n }\n if (error instanceof PluginNotFoundError) {\n return {\n status_code: ErrorCodes.PLUGIN_NOT_FOUND,\n error_msg: `Plugin not found, please install plugin: ${error.pluginKey}`,\n };\n }\n if (error instanceof ActionNotFoundError) {\n return {\n status_code: ErrorCodes.ACTION_NOT_FOUND,\n error_msg: `Action '${error.actionName}' not found in plugin ${error.pluginKey}`,\n };\n }\n return {\n status_code: ErrorCodes.EXECUTION_ERROR,\n error_msg: `Execution failed: ${error instanceof Error ? error.message : String(error)}`,\n };\n }\n\n @Post('debug/:capability_id/stream')\n async debugStream(\n @Param('capability_id') capabilityId: string,\n @Body() body: DebugRequestBody,\n @Res() res: Response,\n ): Promise<void> {\n const params = body.params ?? {};\n\n // 设置 SSE 响应头\n res.setHeader('Content-Type', 'text/event-stream');\n res.setHeader('Cache-Control', 'no-cache');\n res.setHeader('Connection', 'keep-alive');\n\n try {\n const config = this.getCapabilityConfig(capabilityId, body.capability);\n const action = await this.getActionName(config.pluginKey, config.formValue, body.action);\n const loggerContext = {\n capability_id: config.id,\n plugin_key: config.pluginKey,\n action,\n };\n\n // 打印入参\n this.logger.log(`Executing capability (stream)`, {\n ...loggerContext,\n input: stringifyForLog(params),\n });\n\n const capability = this.capabilityService.loadWithConfig(config);\n const eventStream = capability.callStreamWithEvents(action, params, { isDebug: true });\n\n // 延迟发送策略:缓存上一个 chunk,收到下一个 data 或 done 时再发送\n // 这样可以在最后一个 chunk 上标记 finished: true\n let pendingChunk: unknown | null = null;\n\n for await (const event of eventStream) {\n switch (event.type) {\n case 'data': {\n // 先发送上一个缓存的 chunk(如果有)\n if (pendingChunk !== null) {\n const response: StreamContentResponse = {\n status_code: ErrorCodes.SUCCESS,\n data: {\n type: 'content',\n delta: pendingChunk, // 直接透传 OutputSchema\n },\n };\n res.write(`data: ${JSON.stringify(response)}\\n\\n`);\n }\n // 缓存当前 chunk\n pendingChunk = event.data;\n break;\n }\n\n case 'done': {\n // 发送最后一个 chunk 并带上 finished: true\n if (pendingChunk !== null) {\n const response: StreamContentResponse = {\n status_code: ErrorCodes.SUCCESS,\n data: {\n type: 'content',\n delta: pendingChunk, // 直接透传 OutputSchema\n finished: true,\n },\n };\n res.write(`data: ${JSON.stringify(response)}\\n\\n`);\n }\n // 打印聚合结果(从 done 事件的 metadata 中获取)\n this.logger.log(`Capability (stream) executed successfully`, {\n ...loggerContext,\n duration_ms: event.metadata.duration,\n output: stringifyForLog(event.metadata.aggregated),\n });\n res.end();\n return;\n }\n\n case 'error': {\n // 错误事件:发送错误信息\n const response: StreamErrorResponse = {\n status_code: ErrorCodes.SUCCESS,\n data: {\n type: 'error',\n error: {\n code: 0,\n message: event.error.message,\n },\n },\n };\n res.write(`data: ${JSON.stringify(response)}\\n\\n`);\n res.end();\n return;\n }\n }\n }\n\n // 如果事件流正常结束但没有 done 事件,发送缓存的 chunk\n if (pendingChunk !== null) {\n const response: StreamContentResponse = {\n status_code: ErrorCodes.SUCCESS,\n data: {\n type: 'content',\n delta: pendingChunk, // 直接透传 OutputSchema\n finished: true,\n },\n };\n res.write(`data: ${JSON.stringify(response)}\\n\\n`);\n }\n res.end();\n } catch (error) {\n // 流异常中断(在事件流开始前的错误)\n const errorMsg = error instanceof Error ? error.message : String(error);\n const response: StreamErrorResponse = {\n status_code: ErrorCodes.SUCCESS,\n data: {\n type: 'error',\n error: {\n code: 0,\n message: errorMsg,\n },\n },\n };\n res.write(`data: ${JSON.stringify(response)}\\n\\n`);\n res.end();\n }\n }\n}\n","import {\n Controller,\n Get,\n Post,\n Param,\n Body,\n Res,\n HttpStatus,\n Logger,\n} from '@nestjs/common';\nimport { ApiExcludeController } from '@nestjs/swagger';\nimport type { Response } from 'express';\nimport {\n CapabilityService,\n CapabilityNotFoundError,\n ActionNotFoundError,\n} from '../services/capability.service';\nimport { PluginNotFoundError } from '../services/plugin-loader.service';\nimport type {\n SuccessResponse,\n ErrorResponse,\n ExecuteResponseData,\n ListResponseData,\n StreamContentResponse,\n StreamErrorResponse,\n} from '../interfaces';\nimport { ErrorCodes } from '../interfaces';\nimport { stringifyForLog } from '../utils';\n\ninterface ExecuteRequestBody {\n action: string;\n params: Record<string, unknown>;\n}\n@ApiExcludeController()\n@Controller('api/capability')\nexport class WebhookController {\n private readonly logger = new Logger(WebhookController.name);\n\n constructor(private readonly capabilityService: CapabilityService) {}\n\n @Get('list')\n list(@Res() res: Response): void {\n try {\n const capabilities = this.capabilityService.listCapabilities();\n res.status(HttpStatus.OK).json({\n status_code: ErrorCodes.SUCCESS,\n data: {\n capabilities: capabilities.map(c => ({\n id: c.id,\n name: c.name,\n pluginID: c.pluginKey,\n pluginVersion: c.pluginVersion,\n })),\n },\n } satisfies SuccessResponse<ListResponseData>);\n } catch (error) {\n this.logger.error('Failed to list capabilities', error);\n res.status(HttpStatus.INTERNAL_SERVER_ERROR).json({\n status_code: ErrorCodes.EXECUTION_ERROR,\n error_msg: `Failed to list capabilities: ${error instanceof Error ? error.message : String(error)}`,\n } satisfies ErrorResponse);\n }\n }\n\n @Post(':capability_id')\n async execute(\n @Param('capability_id') capabilityId: string,\n @Body() body: ExecuteRequestBody,\n @Res() res: Response,\n ): Promise<void> {\n try {\n const result = await this.capabilityService\n .load(capabilityId)\n .call(body.action, body.params);\n\n res.status(HttpStatus.OK).json({\n status_code: ErrorCodes.SUCCESS,\n data: {\n output: result,\n },\n } satisfies SuccessResponse<ExecuteResponseData>);\n } catch (error) {\n this.logger.error('Capability execution failed', error);\n res.status(HttpStatus.INTERNAL_SERVER_ERROR).json(\n this.buildErrorResponse(error),\n );\n }\n }\n\n /**\n * 构建错误响应\n */\n private buildErrorResponse(error: unknown): ErrorResponse {\n if (error instanceof CapabilityNotFoundError) {\n return {\n status_code: ErrorCodes.CAPABILITY_NOT_FOUND,\n error_msg: `Capability not found: ${error.capabilityId}`,\n };\n }\n if (error instanceof PluginNotFoundError) {\n return {\n status_code: ErrorCodes.PLUGIN_NOT_FOUND,\n error_msg: `Plugin not found, please install plugin: ${error.pluginKey}`,\n };\n }\n if (error instanceof ActionNotFoundError) {\n return {\n status_code: ErrorCodes.ACTION_NOT_FOUND,\n error_msg: `Action '${error.actionName}' not found in plugin ${error.pluginKey}`,\n };\n }\n return {\n status_code: ErrorCodes.EXECUTION_ERROR,\n error_msg: `Execution failed: ${error instanceof Error ? error.message : String(error)}`,\n };\n }\n\n @Post(':capability_id/stream')\n async executeStream(\n @Param('capability_id') capabilityId: string,\n @Body() body: ExecuteRequestBody,\n @Res() res: Response,\n ): Promise<void> {\n const loggerContext = {\n capability_id: capabilityId,\n action: body.action,\n };\n\n // 设置 SSE 响应头\n res.setHeader('Content-Type', 'text/event-stream');\n res.setHeader('Cache-Control', 'no-cache');\n res.setHeader('Connection', 'keep-alive');\n\n try {\n // 打印入参\n this.logger.log(`Executing capability (stream)`, {\n ...loggerContext,\n input: stringifyForLog(body.params),\n });\n\n const capability = this.capabilityService.load(capabilityId);\n const eventStream = capability.callStreamWithEvents(body.action, body.params);\n\n // 延迟发送策略:缓存上一个 chunk,收到下一个 data 或 done 时再发送\n // 这样可以在最后一个 chunk 上标记 finished: true\n let pendingChunk: unknown | null = null;\n\n for await (const event of eventStream) {\n switch (event.type) {\n case 'data': {\n // 先发送上一个缓存的 chunk(如果有)\n if (pendingChunk !== null) {\n const response: StreamContentResponse = {\n status_code: ErrorCodes.SUCCESS,\n data: {\n type: 'content',\n delta: pendingChunk, // 直接透传 OutputSchema\n },\n };\n res.write(`data: ${JSON.stringify(response)}\\n\\n`);\n }\n // 缓存当前 chunk\n pendingChunk = event.data;\n break;\n }\n\n case 'done': {\n // 发送最后一个 chunk 并带上 finished: true\n if (pendingChunk !== null) {\n const response: StreamContentResponse = {\n status_code: ErrorCodes.SUCCESS,\n data: {\n type: 'content',\n delta: pendingChunk, // 直接透传 OutputSchema\n finished: true,\n },\n };\n res.write(`data: ${JSON.stringify(response)}\\n\\n`);\n }\n // 打印聚合结果(从 done 事件的 metadata 中获取)\n this.logger.log(`Capability (stream) executed successfully`, {\n ...loggerContext,\n duration_ms: event.metadata.duration,\n output: stringifyForLog(event.metadata.aggregated),\n });\n res.end();\n return;\n }\n\n case 'error': {\n // 错误事件:发送错误信息\n const response: StreamErrorResponse = {\n status_code: ErrorCodes.SUCCESS,\n data: {\n type: 'error',\n error: {\n code: 0,\n message: event.error.message,\n },\n },\n };\n res.write(`data: ${JSON.stringify(response)}\\n\\n`);\n res.end();\n return;\n }\n }\n }\n\n // 如果事件流正常结束但没有 done 事件,发送缓存的 chunk\n if (pendingChunk !== null) {\n const response: StreamContentResponse = {\n status_code: ErrorCodes.SUCCESS,\n data: {\n type: 'content',\n delta: pendingChunk, // 直接透传 OutputSchema\n finished: true,\n },\n };\n res.write(`data: ${JSON.stringify(response)}\\n\\n`);\n }\n res.end();\n } catch (error) {\n // 流异常中断(在事件流开始前的错误)\n const errorMsg = error instanceof Error ? error.message : String(error);\n const response: StreamErrorResponse = {\n status_code: ErrorCodes.SUCCESS,\n data: {\n type: 'error',\n error: {\n code: 0,\n message: errorMsg,\n },\n },\n };\n res.write(`data: ${JSON.stringify(response)}\\n\\n`);\n res.end();\n }\n }\n}\n","import { Module, DynamicModule, Type } from '@nestjs/common';\nimport {\n CommonModule,\n RequestContextService,\n PLATFORM_HTTP_CLIENT,\n} from '@lark-apaas/nestjs-common';\nimport { DebugController, WebhookController } from './controllers';\nimport {\n CapabilityService,\n PluginLoaderService,\n TemplateEngineService,\n type CapabilityModuleOptions,\n} from './services';\n\nconst CAPABILITY_OPTIONS = Symbol('CAPABILITY_OPTIONS');\n\nconst isDevelopment = process.env.NODE_ENV === 'development';\n\nfunction getControllers(): Type[] {\n const controllers: Type[] = [WebhookController];\n if (isDevelopment) {\n controllers.push(DebugController);\n }\n return controllers;\n}\n\n@Module({\n imports: [CommonModule],\n controllers: getControllers(),\n providers: [CapabilityService, PluginLoaderService, TemplateEngineService],\n exports: [CapabilityService],\n})\nexport class CapabilityModule {\n static forRoot(options?: CapabilityModuleOptions): DynamicModule {\n return {\n module: CapabilityModule,\n imports: [CommonModule],\n controllers: getControllers(),\n providers: [\n {\n provide: CAPABILITY_OPTIONS,\n useValue: options ?? {},\n },\n {\n provide: CapabilityService,\n useFactory: (\n requestContextService: RequestContextService,\n httpClient: any,\n pluginLoader: PluginLoaderService,\n templateEngine: TemplateEngineService,\n moduleOptions: CapabilityModuleOptions,\n ) => {\n const service = new CapabilityService(\n requestContextService,\n httpClient,\n pluginLoader,\n templateEngine,\n );\n // 设置模块配置(包括 capabilitiesDir、enableWatching、watchDebounce)\n if (moduleOptions) {\n service.setOptions(moduleOptions);\n }\n return service;\n },\n inject: [\n RequestContextService,\n PLATFORM_HTTP_CLIENT,\n PluginLoaderService,\n TemplateEngineService,\n CAPABILITY_OPTIONS,\n ],\n },\n PluginLoaderService,\n TemplateEngineService,\n ],\n exports: [CapabilityService],\n };\n }\n}\n"],"mappings":"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","names":["getImportMetaUrl","document","URL","__filename","href","currentScript","tagName","toUpperCase","src","baseURI","importMetaUrl","ErrorCodes","SUCCESS","CAPABILITY_NOT_FOUND","PLUGIN_NOT_FOUND","ACTION_NOT_FOUND","PARAMS_VALIDATION_ERROR","EXECUTION_ERROR","TYPE_DEFAULT_VALUES","array","object","string","number","integer","boolean","null","TemplateEngineService","EXPR_REGEX","WHOLE_STRING_EXPR_REGEX","resolve","template","input","paramsSchema","resolveString","Array","isArray","map","item","resolveObject","wholeMatch","match","path","value","getValueByPath","undefined","defaultValue","getDefaultValueForPath","lastIndex","test","result","replace","String","key","Object","entries","obj","keys","split","current","schema","fieldSchema","getSchemaForPath","getDefaultValueFromSchema","currentSchema","properties","default","oneOf","anyOf","type","t","import_common","require","createRequire","PluginNotFoundError","Error","pluginKey","name","PluginLoadError","reason","PluginLoaderService","logger","Logger","pluginInstances","Map","pluginVersions","manifestCache","findPackageRoot","entryPath","dir","dirname","existsSync","join","getManifest","has","get","resolve","packageRoot","set","manifestPath","content","readFileSync","manifest","JSON","parse","isConfigRequired","form","refType","loadPlugin","config","createPluginInstance","cached","debug","instance","log","undefined","resolvedPath","version","fileUrl","pathToFileURL","href","importPath","mod","pluginPackage","default","create","error","code","message","String","isPluginInstalled","clearCache","delete","clearNodeModuleCache","Date","now","key","keys","clear","cache","clearModuleAndChildren","pluginScope","startsWith","split","slice","children","forEach","child","filename","includes","reloadPlugin","import_common","fs","path","DEFAULT_MAX_LENGTH","truncateString","str","maxLength","length","slice","stringifyForLog","value","JSON","stringify","String","migrationAdaptor","promise","result","code","data","output","outcome","error","errorMessage","Error","message","String","appStatusCode","failureOutput","response","status","protocolStatusCode","body","JSON","stringify","stack","undefined","CapabilityNotFoundError","Error","capabilityId","name","ActionNotFoundError","pluginKey","actionName","CapabilityService","logger","Logger","capabilities","Map","filePathToId","capabilitiesDir","fileWatcher","options","requestContextService","platformHttpClient","pluginLoaderService","templateEngineService","isDev","process","env","NODE_ENV","join","cwd","setOptions","setCapabilitiesDir","dir","onModuleInit","loadCapabilities","enableWatching","startWatching","onModuleDestroy","stopWatching","existsSync","warn","pattern","debounce","watchDebounce","log","watch","persistent","ignoreInitial","awaitWriteFinish","stabilityThreshold","pollInterval","on","filePath","handleFileAdd","handleFileChange","handleFileUnlink","error","close","reloadAllCapabilities","clear","loadCapabilityFromFile","get","config","clearCache","reloadCapabilityFromFile","removeCapabilityByFile","content","readFileSync","JSON","parse","id","set","oldId","oldConfig","delete","files","readdirSync","filter","f","endsWith","file","size","listCapabilities","Array","from","values","getCapability","load","createExecutor","loadWithConfig","call","input","contextOverride","executeCall","callStream","executeCallStream","callStreamWithEvents","executeCallStreamWithEvents","isStream","checkIsStream","loadPluginAndResolveParams","isConfigRequired","pluginInstance","loadPlugin","formValue","resolvedParams","resolve","paramsSchema","hasAction","isStreamAction","startTime","Date","now","loggerContext","capability_id","plugin_key","action","context","buildActionContext","is_stream","stringifyForLog","result","runStream","chunks","chunk","push","aggregate","run","duration_ms","output","message","String","aggregatedResult","runStreamWithEvents","event","type","data","metadata","aggregated","length","duration","code","override","userContext","getUserContext","isDebug","ctx","getContext","appId","userId","tenantId","import_common","DebugController","logger","Logger","name","capabilityService","pluginLoaderService","templateEngineService","list","res","capabilities","listCapabilities","status","HttpStatus","OK","json","status_code","ErrorCodes","SUCCESS","data","map","c","id","pluginID","pluginKey","pluginVersion","error","INTERNAL_SERVER_ERROR","EXECUTION_ERROR","error_msg","Error","message","String","getCapabilityConfig","capabilityId","bodyCapability","config","getCapability","CapabilityNotFoundError","getActionName","formValue","bodyAction","isConfigRequired","pluginInstance","loadPlugin","actions","listActions","length","debug","body","startTime","Date","now","params","capability","action","resolvedParams","resolve","paramsSchema","result","loadWithConfig","call","isDebug","output","capabilityConfig","duration","buildErrorResponse","CAPABILITY_NOT_FOUND","PluginNotFoundError","PLUGIN_NOT_FOUND","ActionNotFoundError","ACTION_NOT_FOUND","actionName","debugStream","setHeader","loggerContext","capability_id","plugin_key","log","input","stringifyForLog","eventStream","callStreamWithEvents","pendingChunk","event","type","response","delta","write","JSON","stringify","finished","duration_ms","metadata","aggregated","end","code","errorMsg","import_common","import_swagger","WebhookController","logger","Logger","name","capabilityService","list","res","capabilities","listCapabilities","status","HttpStatus","OK","json","status_code","ErrorCodes","SUCCESS","data","map","c","id","pluginID","pluginKey","pluginVersion","error","INTERNAL_SERVER_ERROR","EXECUTION_ERROR","error_msg","Error","message","String","execute","capabilityId","body","result","load","call","action","params","output","buildErrorResponse","CapabilityNotFoundError","CAPABILITY_NOT_FOUND","PluginNotFoundError","PLUGIN_NOT_FOUND","ActionNotFoundError","ACTION_NOT_FOUND","actionName","executeStream","loggerContext","capability_id","setHeader","log","input","stringifyForLog","capability","eventStream","callStreamWithEvents","pendingChunk","event","type","response","delta","write","JSON","stringify","finished","duration_ms","metadata","duration","aggregated","end","code","errorMsg","import_common","import_nestjs_common","CAPABILITY_OPTIONS","Symbol","isDevelopment","process","env","NODE_ENV","getControllers","controllers","WebhookController","push","DebugController","CapabilityModule","forRoot","options","module","imports","CommonModule","providers","provide","useValue","CapabilityService","useFactory","requestContextService","httpClient","pluginLoader","templateEngine","moduleOptions","service","setOptions","inject","RequestContextService","PLATFORM_HTTP_CLIENT","PluginLoaderService","TemplateEngineService","exports"]}
package/dist/index.d.cts CHANGED
@@ -178,7 +178,7 @@ interface PluginInstance {
178
178
  aggregate?(actionName: string, chunks: unknown[]): unknown;
179
179
  }
180
180
  interface PluginPackage {
181
- create(): PluginInstance;
181
+ create(config?: unknown): PluginInstance;
182
182
  }
183
183
 
184
184
  /**
@@ -258,6 +258,14 @@ declare class TemplateEngineService {
258
258
  private getDefaultValueFromSchema;
259
259
  }
260
260
 
261
+ interface PluginManifest {
262
+ name: string;
263
+ form?: {
264
+ refType?: 'config' | 'action';
265
+ schema?: unknown;
266
+ };
267
+ [key: string]: unknown;
268
+ }
261
269
  declare class PluginNotFoundError extends Error {
262
270
  readonly pluginKey: string;
263
271
  constructor(pluginKey: string);
@@ -270,11 +278,30 @@ declare class PluginLoaderService {
270
278
  private readonly pluginInstances;
271
279
  /** 记录每个插件的加载版本(时间戳),用于 ESM 缓存绕过 */
272
280
  private readonly pluginVersions;
273
- loadPlugin(pluginKey: string): Promise<PluginInstance>;
281
+ /** 缓存插件的 manifest.json */
282
+ private readonly manifestCache;
283
+ /**
284
+ * 从入口路径向上查找包根目录(包含 package.json 的目录)
285
+ */
286
+ private findPackageRoot;
287
+ /**
288
+ * 读取并缓存 manifest
289
+ */
290
+ getManifest(pluginKey: string): PluginManifest | null;
291
+ /**
292
+ * 检查插件是否需要 config(form.refType === 'config')
293
+ */
294
+ isConfigRequired(pluginKey: string): boolean;
295
+ loadPlugin(pluginKey: string, config?: unknown): Promise<PluginInstance>;
296
+ /**
297
+ * 创建插件实例(内部方法)
298
+ */
299
+ private createPluginInstance;
274
300
  isPluginInstalled(pluginKey: string): boolean;
275
301
  /**
276
302
  * 清除插件缓存
277
303
  * - 清除应用层 pluginInstances 缓存
304
+ * - 清除 manifest 缓存
278
305
  * - 清除 Node.js CJS 模块缓存(require.cache)
279
306
  * - 更新版本号,下次 import 时绕过 ESM 缓存
280
307
  * @param pluginKey - 插件标识,不传则清除所有
@@ -394,6 +421,11 @@ declare class CapabilityService implements OnModuleInit, OnModuleDestroy {
394
421
  */
395
422
  loadWithConfig(config: CapabilityConfig): CapabilityExecutor;
396
423
  private createExecutor;
424
+ /**
425
+ * 加载插件实例并解析参数
426
+ * 根据 manifest 的 form.refType 决定 formValue 的消费方式
427
+ */
428
+ private loadPluginAndResolveParams;
397
429
  /**
398
430
  * 检查 action 是否为流式
399
431
  */
@@ -535,4 +567,4 @@ interface MigrationResponse {
535
567
  */
536
568
  declare function migrationAdaptor(promise: Promise<unknown>): Promise<MigrationResponse>;
537
569
 
538
- export { ActionNotFoundError, type ActionSchema, type CapabilityConfig, type CapabilityExecutor, type CapabilityListItem, CapabilityModule, type CapabilityModuleOptions, CapabilityNotFoundError, CapabilityService, DebugController, type DebugExecuteResponseData, type DebugInfo, type ErrorCode, ErrorCodes, type ErrorResponse, type ExecuteResponseData, type FailureOutput, type JSONSchema, type ListResponseData, type MigrationResponse, type PluginActionContext, type PluginInstance, PluginLoadError, PluginLoaderService, PluginNotFoundError, type PluginPackage, type StreamContentResponse, type StreamDoneMetadata, type StreamError, type StreamErrorResponse, type StreamEvent, type StreamResponse, type SuccessResponse, TemplateEngineService, type UserContext, WebhookController, migrationAdaptor };
570
+ export { ActionNotFoundError, type ActionSchema, type CapabilityConfig, type CapabilityExecutor, type CapabilityListItem, CapabilityModule, type CapabilityModuleOptions, CapabilityNotFoundError, CapabilityService, DebugController, type DebugExecuteResponseData, type DebugInfo, type ErrorCode, ErrorCodes, type ErrorResponse, type ExecuteResponseData, type FailureOutput, type JSONSchema, type ListResponseData, type MigrationResponse, type PluginActionContext, type PluginInstance, PluginLoadError, PluginLoaderService, type PluginManifest, PluginNotFoundError, type PluginPackage, type StreamContentResponse, type StreamDoneMetadata, type StreamError, type StreamErrorResponse, type StreamEvent, type StreamResponse, type SuccessResponse, TemplateEngineService, type UserContext, WebhookController, migrationAdaptor };