@lark-apaas/nestjs-capability 0.0.1-alpha.7 → 0.0.1-alpha.8
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +16 -0
- package/dist/index.cjs +41 -16
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +2 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +41 -16
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -93,14 +93,23 @@ const result = await this.capabilityService
|
|
|
93
93
|
### 上下文覆盖
|
|
94
94
|
|
|
95
95
|
```typescript
|
|
96
|
+
// 覆盖 userContext
|
|
96
97
|
const result = await this.capabilityService
|
|
97
98
|
.load('notify_task_created')
|
|
98
99
|
.call('run', inputParams, {
|
|
99
100
|
userContext: {
|
|
100
101
|
userId: 'custom-user-id',
|
|
101
102
|
tenantId: 'custom-tenant-id',
|
|
103
|
+
appId: 'custom-app-id',
|
|
102
104
|
},
|
|
103
105
|
});
|
|
106
|
+
|
|
107
|
+
// 设置调试模式(插件可据此返回更详细的错误信息)
|
|
108
|
+
const debugResult = await this.capabilityService
|
|
109
|
+
.load('ai_chat')
|
|
110
|
+
.call('chat', inputParams, {
|
|
111
|
+
isDebug: true,
|
|
112
|
+
});
|
|
104
113
|
```
|
|
105
114
|
|
|
106
115
|
## 能力配置
|
|
@@ -377,10 +386,17 @@ interface PluginActionContext {
|
|
|
377
386
|
userContext: {
|
|
378
387
|
userId: string; // 用户 ID
|
|
379
388
|
tenantId: string; // 租户 ID
|
|
389
|
+
appId: string; // 应用 ID
|
|
380
390
|
};
|
|
391
|
+
isDebug: boolean; // 是否为调试模式
|
|
381
392
|
}
|
|
382
393
|
```
|
|
383
394
|
|
|
395
|
+
**isDebug 说明:**
|
|
396
|
+
- `DebugController` 调用时 `isDebug = true`
|
|
397
|
+
- `WebhookController` 或其他调用时 `isDebug = false`
|
|
398
|
+
- 插件可据此返回更详细的错误信息、调试日志等
|
|
399
|
+
|
|
384
400
|
## 错误类型
|
|
385
401
|
|
|
386
402
|
| 错误 | 描述 |
|
package/dist/index.cjs
CHANGED
|
@@ -499,8 +499,19 @@ var CapabilityService = class _CapabilityService {
|
|
|
499
499
|
for await (const event of pluginInstance.runStreamWithEvents(actionName, context, resolvedParams)) {
|
|
500
500
|
if (event.type === "data") {
|
|
501
501
|
chunks.push(event.data);
|
|
502
|
+
yield event;
|
|
503
|
+
} else if (event.type === "done") {
|
|
504
|
+
const aggregatedResult2 = pluginInstance.aggregate ? pluginInstance.aggregate(actionName, chunks) : chunks;
|
|
505
|
+
yield {
|
|
506
|
+
type: "done",
|
|
507
|
+
metadata: {
|
|
508
|
+
...event.metadata,
|
|
509
|
+
aggregated: aggregatedResult2
|
|
510
|
+
}
|
|
511
|
+
};
|
|
512
|
+
} else {
|
|
513
|
+
yield event;
|
|
502
514
|
}
|
|
503
|
-
yield event;
|
|
504
515
|
}
|
|
505
516
|
} else {
|
|
506
517
|
if (isStream && pluginInstance.runStream) {
|
|
@@ -519,11 +530,13 @@ var CapabilityService = class _CapabilityService {
|
|
|
519
530
|
data: result
|
|
520
531
|
};
|
|
521
532
|
}
|
|
533
|
+
const aggregatedResult2 = pluginInstance.aggregate ? pluginInstance.aggregate(actionName, chunks) : chunks;
|
|
522
534
|
yield {
|
|
523
535
|
type: "done",
|
|
524
536
|
metadata: {
|
|
525
537
|
chunks: chunks.length,
|
|
526
|
-
duration: Date.now() - startTime
|
|
538
|
+
duration: Date.now() - startTime,
|
|
539
|
+
aggregated: aggregatedResult2
|
|
527
540
|
}
|
|
528
541
|
};
|
|
529
542
|
}
|
|
@@ -552,7 +565,8 @@ var CapabilityService = class _CapabilityService {
|
|
|
552
565
|
return {
|
|
553
566
|
logger: this.logger,
|
|
554
567
|
platformHttpClient: this.platformHttpClient,
|
|
555
|
-
userContext: override?.userContext ?? this.getUserContext()
|
|
568
|
+
userContext: override?.userContext ?? this.getUserContext(),
|
|
569
|
+
isDebug: override?.isDebug ?? false
|
|
556
570
|
};
|
|
557
571
|
}
|
|
558
572
|
getUserContext() {
|
|
@@ -666,7 +680,9 @@ var DebugController = class _DebugController {
|
|
|
666
680
|
const action = await this.getActionName(config.pluginKey, body.action);
|
|
667
681
|
const resolvedParams = this.templateEngineService.resolve(config.formValue, params);
|
|
668
682
|
try {
|
|
669
|
-
const result = await this.capabilityService.loadWithConfig(config).call(action, params
|
|
683
|
+
const result = await this.capabilityService.loadWithConfig(config).call(action, params, {
|
|
684
|
+
isDebug: true
|
|
685
|
+
});
|
|
670
686
|
return {
|
|
671
687
|
status_code: ErrorCodes.SUCCESS,
|
|
672
688
|
data: {
|
|
@@ -707,7 +723,6 @@ var DebugController = class _DebugController {
|
|
|
707
723
|
}
|
|
708
724
|
async debugStream(capabilityId, body, res) {
|
|
709
725
|
const params = body.params ?? {};
|
|
710
|
-
const startTime = Date.now();
|
|
711
726
|
res.setHeader("Content-Type", "text/event-stream");
|
|
712
727
|
res.setHeader("Cache-Control", "no-cache");
|
|
713
728
|
res.setHeader("Connection", "keep-alive");
|
|
@@ -719,15 +734,18 @@ var DebugController = class _DebugController {
|
|
|
719
734
|
plugin_key: config.pluginKey,
|
|
720
735
|
action
|
|
721
736
|
};
|
|
722
|
-
this.logger.log(`Executing capability (stream)
|
|
737
|
+
this.logger.log(`Executing capability (stream)`, {
|
|
738
|
+
...loggerContext,
|
|
739
|
+
input: stringifyForLog(params)
|
|
740
|
+
});
|
|
723
741
|
const capability = this.capabilityService.loadWithConfig(config);
|
|
724
|
-
const eventStream = capability.callStreamWithEvents(action, params
|
|
742
|
+
const eventStream = capability.callStreamWithEvents(action, params, {
|
|
743
|
+
isDebug: true
|
|
744
|
+
});
|
|
725
745
|
let pendingChunk = null;
|
|
726
|
-
const allChunks = [];
|
|
727
746
|
for await (const event of eventStream) {
|
|
728
747
|
switch (event.type) {
|
|
729
748
|
case "data": {
|
|
730
|
-
allChunks.push(event.data);
|
|
731
749
|
if (pendingChunk !== null) {
|
|
732
750
|
const response = {
|
|
733
751
|
status_code: ErrorCodes.SUCCESS,
|
|
@@ -761,7 +779,11 @@ var DebugController = class _DebugController {
|
|
|
761
779
|
|
|
762
780
|
`);
|
|
763
781
|
}
|
|
764
|
-
this.logger.log(`Capability (stream) executed successfully
|
|
782
|
+
this.logger.log(`Capability (stream) executed successfully`, {
|
|
783
|
+
...loggerContext,
|
|
784
|
+
duration_ms: event.metadata.duration,
|
|
785
|
+
output: stringifyForLog(event.metadata.aggregated)
|
|
786
|
+
});
|
|
765
787
|
res.end();
|
|
766
788
|
return;
|
|
767
789
|
}
|
|
@@ -936,7 +958,6 @@ var WebhookController = class _WebhookController {
|
|
|
936
958
|
}
|
|
937
959
|
}
|
|
938
960
|
async executeStream(capabilityId, body, res) {
|
|
939
|
-
const startTime = Date.now();
|
|
940
961
|
const loggerContext = {
|
|
941
962
|
capability_id: capabilityId,
|
|
942
963
|
action: body.action
|
|
@@ -945,15 +966,16 @@ var WebhookController = class _WebhookController {
|
|
|
945
966
|
res.setHeader("Cache-Control", "no-cache");
|
|
946
967
|
res.setHeader("Connection", "keep-alive");
|
|
947
968
|
try {
|
|
948
|
-
this.logger.log(`Executing capability (stream)
|
|
969
|
+
this.logger.log(`Executing capability (stream)`, {
|
|
970
|
+
...loggerContext,
|
|
971
|
+
input: stringifyForLog(body.params)
|
|
972
|
+
});
|
|
949
973
|
const capability = this.capabilityService.load(capabilityId);
|
|
950
974
|
const eventStream = capability.callStreamWithEvents(body.action, body.params);
|
|
951
975
|
let pendingChunk = null;
|
|
952
|
-
const allChunks = [];
|
|
953
976
|
for await (const event of eventStream) {
|
|
954
977
|
switch (event.type) {
|
|
955
978
|
case "data": {
|
|
956
|
-
allChunks.push(event.data);
|
|
957
979
|
if (pendingChunk !== null) {
|
|
958
980
|
const response = {
|
|
959
981
|
status_code: ErrorCodes.SUCCESS,
|
|
@@ -987,7 +1009,11 @@ var WebhookController = class _WebhookController {
|
|
|
987
1009
|
|
|
988
1010
|
`);
|
|
989
1011
|
}
|
|
990
|
-
this.logger.log(`Capability (stream) executed successfully
|
|
1012
|
+
this.logger.log(`Capability (stream) executed successfully`, {
|
|
1013
|
+
...loggerContext,
|
|
1014
|
+
duration_ms: event.metadata.duration,
|
|
1015
|
+
output: stringifyForLog(event.metadata.aggregated)
|
|
1016
|
+
});
|
|
991
1017
|
res.end();
|
|
992
1018
|
return;
|
|
993
1019
|
}
|
|
@@ -1025,7 +1051,6 @@ var WebhookController = class _WebhookController {
|
|
|
1025
1051
|
|
|
1026
1052
|
`);
|
|
1027
1053
|
}
|
|
1028
|
-
this.logger.log(`Capability (stream) executed successfully, duration: ${Date.now() - startTime}ms, chunks: ${allChunks.length}, result: ${stringifyForLog(allChunks)}`, loggerContext);
|
|
1029
1054
|
res.end();
|
|
1030
1055
|
} catch (error) {
|
|
1031
1056
|
const errorMsg = error instanceof Error ? error.message : String(error);
|
package/dist/index.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/index.ts","../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/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';\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';\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 */\n@Injectable()\nexport class TemplateEngineService {\n // 匹配 {{input.xxx}} 或 {{input.xxx.yyy}} 表达式\n private readonly EXPR_REGEX = /\\{\\{input\\.([a-zA-Z_][a-zA-Z_0-9]*(?:\\.[a-zA-Z_][a-zA-Z_0-9]*)*)\\}\\}/g;\n // 匹配整串单个表达式\n private readonly WHOLE_STRING_EXPR_REGEX = /^\\{\\{input\\.([a-zA-Z_][a-zA-Z_0-9]*(?:\\.[a-zA-Z_][a-zA-Z_0-9]*)*)\\}\\}$/;\n\n resolve(template: unknown, input: Record<string, unknown>): unknown {\n if (typeof template === 'string') {\n return this.resolveString(template, input);\n }\n\n if (Array.isArray(template)) {\n return template.map(item => this.resolve(item, input));\n }\n\n if (template !== null && typeof template === 'object') {\n return this.resolveObject(template as Record<string, unknown>, input);\n }\n\n return template;\n }\n\n private resolveString(template: string, input: Record<string, unknown>): 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 return value !== undefined ? value : template;\n }\n\n // 情况2: 字符串插值 → 返回字符串\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 ): 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);\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","import { Injectable, Logger } from '@nestjs/common';\nimport type { PluginInstance, PluginPackage } from '../interfaces';\n\nexport class PluginNotFoundError extends Error {\n constructor(pluginKey: string) {\n super(`Plugin not found: ${pluginKey}`);\n this.name = 'PluginNotFoundError';\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\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 // eslint-disable-next-line @typescript-eslint/no-require-imports\n const pluginPackage = (await import(pluginKey)).default 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 clearCache(pluginKey?: string): void {\n if (pluginKey) {\n this.pluginInstances.delete(pluginKey);\n this.logger.log(`Cleared cache for plugin: ${pluginKey}`);\n } else {\n this.pluginInstances.clear();\n this.logger.log('Cleared all plugin caches');\n }\n }\n}\n","import { Injectable, Logger, Inject, OnModuleInit } 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 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 constructor(capabilityId: string) {\n super(`Capability not found: ${capabilityId}`);\n this.name = 'CapabilityNotFoundError';\n }\n}\n\nexport class ActionNotFoundError extends Error {\n constructor(pluginKey: string, actionName: string) {\n super(`Action '${actionName}' not found in plugin ${pluginKey}`);\n this.name = 'ActionNotFoundError';\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 capabilitiesDir?: string;\n}\n\n@Injectable()\nexport class CapabilityService implements OnModuleInit {\n private readonly logger = new Logger(CapabilityService.name);\n private readonly capabilities = new Map<string, CapabilityConfig>();\n private capabilitiesDir: string;\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 this.capabilitiesDir = path.join(process.cwd(), 'server/capabilities');\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 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 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: ${file}`);\n continue;\n }\n\n this.capabilities.set(config.id, config);\n this.logger.log(`Loaded capability: ${config.id} (${config.name})`);\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>)\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>)\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>)\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 }\n yield event;\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 // 发送 done 事件\n yield {\n type: 'done',\n metadata: {\n chunks: chunks.length,\n duration: Date.now() - startTime,\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 };\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","import {\n Controller,\n Post,\n Get,\n Param,\n Body,\n Res,\n HttpException,\n HttpStatus,\n Logger,\n} from '@nestjs/common';\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@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(): SuccessResponse<ListResponseData> {\n const capabilities = this.capabilityService.listCapabilities();\n\n return {\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 };\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 HttpException(\n {\n code: 1,\n message: `Capability not found: ${capabilityId}`,\n error: 'CAPABILITY_NOT_FOUND',\n },\n HttpStatus.NOT_FOUND,\n );\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 HttpException(\n {\n code: 1,\n message: `Plugin ${pluginKey} has no actions`,\n error: 'NO_ACTIONS',\n },\n HttpStatus.BAD_REQUEST,\n );\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 ): Promise<SuccessResponse<DebugExecuteResponseData> | ErrorResponse> {\n const startTime = Date.now();\n const params = body.params ?? {};\n\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 );\n\n try {\n const result = await this.capabilityService\n .loadWithConfig(config)\n .call(action, params);\n\n return {\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 };\n } catch (error) {\n if (error instanceof CapabilityNotFoundError) {\n throw new HttpException(\n {\n status_code: ErrorCodes.CAPABILITY_NOT_FOUND,\n error_msg: `Capability not found: ${capabilityId}`,\n } satisfies ErrorResponse,\n HttpStatus.NOT_FOUND,\n );\n }\n\n if (error instanceof PluginNotFoundError) {\n throw new HttpException(\n {\n status_code: ErrorCodes.PLUGIN_NOT_FOUND,\n error_msg: `Plugin not found: ${config.pluginKey}`,\n } satisfies ErrorResponse,\n HttpStatus.INTERNAL_SERVER_ERROR,\n );\n }\n\n if (error instanceof ActionNotFoundError) {\n throw new HttpException(\n {\n status_code: ErrorCodes.ACTION_NOT_FOUND,\n error_msg: `Action '${action}' not found in plugin ${config.pluginKey}`,\n } satisfies ErrorResponse,\n HttpStatus.BAD_REQUEST,\n );\n }\n\n throw new HttpException(\n {\n status_code: ErrorCodes.EXECUTION_ERROR,\n error_msg: `Execution failed: ${error instanceof Error ? error.message : String(error)}`,\n } satisfies ErrorResponse,\n HttpStatus.INTERNAL_SERVER_ERROR,\n );\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 const startTime = Date.now();\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), input: ${stringifyForLog(params)}`, loggerContext);\n\n const capability = this.capabilityService.loadWithConfig(config);\n const eventStream = capability.callStreamWithEvents(action, params);\n\n // 延迟发送策略:缓存上一个 chunk,收到下一个 data 或 done 时再发送\n // 这样可以在最后一个 chunk 上标记 finished: true\n let pendingChunk: unknown | null = null;\n // 收集所有 chunk 用于日志\n const allChunks: unknown[] = [];\n\n for await (const event of eventStream) {\n switch (event.type) {\n case 'data': {\n // 收集 chunk 用于日志\n allChunks.push(event.data);\n // 先发送上一个缓存的 chunk(如果有)\n if (pendingChunk !== null) {\n const response: StreamContentResponse = {\n status_code: ErrorCodes.SUCCESS,\n data: {\n type: 'content',\n delta: { content: pendingChunk },\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: { content: pendingChunk },\n finished: true,\n },\n };\n res.write(`data: ${JSON.stringify(response)}\\n\\n`);\n }\n // 打印聚合结果\n this.logger.log(`Capability (stream) executed successfully, duration: ${Date.now() - startTime}ms, chunks: ${allChunks.length}, result: ${stringifyForLog(allChunks)}`, loggerContext);\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: { content: pendingChunk },\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 HttpException,\n HttpStatus,\n Logger,\n} from '@nestjs/common';\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\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(): SuccessResponse<ListResponseData> {\n const capabilities = this.capabilityService.listCapabilities();\n return {\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 };\n }\n\n @Post(':capability_id')\n async execute(\n @Param('capability_id') capabilityId: string,\n @Body() body: ExecuteRequestBody,\n ): Promise<SuccessResponse<ExecuteResponseData> | ErrorResponse> {\n try {\n const result = await this.capabilityService\n .load(capabilityId)\n .call(body.action, body.params);\n\n return {\n status_code: ErrorCodes.SUCCESS,\n data: {\n output: result,\n },\n };\n } catch (error) {\n if (error instanceof CapabilityNotFoundError) {\n throw new HttpException(\n {\n status_code: ErrorCodes.CAPABILITY_NOT_FOUND,\n error_msg: `Capability not found: ${capabilityId}`,\n } satisfies ErrorResponse,\n HttpStatus.NOT_FOUND,\n );\n }\n\n if (error instanceof PluginNotFoundError) {\n throw new HttpException(\n {\n status_code: ErrorCodes.PLUGIN_NOT_FOUND,\n error_msg: `Plugin not found`,\n } satisfies ErrorResponse,\n HttpStatus.INTERNAL_SERVER_ERROR,\n );\n }\n\n if (error instanceof ActionNotFoundError) {\n throw new HttpException(\n {\n status_code: ErrorCodes.ACTION_NOT_FOUND,\n error_msg: `Action '${body.action}' not found`,\n } satisfies ErrorResponse,\n HttpStatus.BAD_REQUEST,\n );\n }\n\n throw new HttpException(\n {\n status_code: ErrorCodes.EXECUTION_ERROR,\n error_msg: `Execution failed: ${error instanceof Error ? error.message : String(error)}`,\n } satisfies ErrorResponse,\n HttpStatus.INTERNAL_SERVER_ERROR,\n );\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 startTime = Date.now();\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), input: ${stringifyForLog(body.params)}`, loggerContext);\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 // 收集所有 chunk 用于日志\n const allChunks: unknown[] = [];\n\n for await (const event of eventStream) {\n switch (event.type) {\n case 'data': {\n // 收集 chunk 用于日志\n allChunks.push(event.data);\n // 先发送上一个缓存的 chunk(如果有)\n if (pendingChunk !== null) {\n const response: StreamContentResponse = {\n status_code: ErrorCodes.SUCCESS,\n data: {\n type: 'content',\n delta: { content: pendingChunk },\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: { content: pendingChunk },\n finished: true,\n },\n };\n res.write(`data: ${JSON.stringify(response)}\\n\\n`);\n }\n // 打印聚合结果\n this.logger.log(`Capability (stream) executed successfully, duration: ${Date.now() - startTime}ms, chunks: ${allChunks.length}, result: ${stringifyForLog(allChunks)}`, loggerContext);\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: { content: pendingChunk },\n finished: true,\n },\n };\n res.write(`data: ${JSON.stringify(response)}\\n\\n`);\n }\n // 打印聚合结果\n this.logger.log(`Capability (stream) executed successfully, duration: ${Date.now() - startTime}ms, chunks: ${allChunks.length}, result: ${stringifyForLog(allChunks)}`, loggerContext);\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 if (moduleOptions?.capabilitiesDir) {\n service.setCapabilitiesDir(moduleOptions.capabilitiesDir);\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":["ErrorCodes","SUCCESS","CAPABILITY_NOT_FOUND","PLUGIN_NOT_FOUND","ACTION_NOT_FOUND","PARAMS_VALIDATION_ERROR","EXECUTION_ERROR","TemplateEngineService","EXPR_REGEX","WHOLE_STRING_EXPR_REGEX","resolve","template","input","resolveString","Array","isArray","map","item","resolveObject","wholeMatch","match","path","value","getValueByPath","undefined","lastIndex","test","result","replace","String","key","Object","entries","obj","keys","split","current","import_common","PluginNotFoundError","Error","pluginKey","name","PluginLoadError","reason","PluginLoaderService","logger","Logger","pluginInstances","Map","loadPlugin","cached","get","debug","log","pluginPackage","default","create","instance","set","error","code","message","String","isPluginInstalled","require","resolve","clearCache","delete","clear","import_common","DEFAULT_MAX_LENGTH","truncateString","str","maxLength","length","slice","stringifyForLog","value","JSON","stringify","String","CapabilityNotFoundError","Error","capabilityId","name","ActionNotFoundError","pluginKey","actionName","CapabilityService","logger","Logger","capabilities","Map","capabilitiesDir","requestContextService","platformHttpClient","pluginLoaderService","templateEngineService","join","process","cwd","setCapabilitiesDir","dir","onModuleInit","loadCapabilities","log","existsSync","warn","files","readdirSync","filter","f","endsWith","file","filePath","content","readFileSync","config","JSON","parse","id","set","error","size","listCapabilities","Array","from","values","getCapability","get","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","context","buildActionContext","is_stream","stringifyForLog","result","runStream","chunks","chunk","push","aggregate","run","duration_ms","output","message","String","aggregatedResult","runStreamWithEvents","event","type","data","metadata","length","duration","code","override","userContext","getUserContext","ctx","getContext","appId","userId","tenantId","import_common","DebugController","logger","Logger","name","capabilityService","pluginLoaderService","templateEngineService","list","capabilities","listCapabilities","status_code","ErrorCodes","SUCCESS","data","map","c","id","pluginID","pluginKey","pluginVersion","getCapabilityConfig","capabilityId","bodyCapability","config","getCapability","HttpException","code","message","error","HttpStatus","NOT_FOUND","getActionName","bodyAction","pluginInstance","loadPlugin","actions","listActions","length","BAD_REQUEST","debug","body","startTime","Date","now","params","capability","action","resolvedParams","resolve","formValue","result","loadWithConfig","call","output","capabilityConfig","duration","CapabilityNotFoundError","CAPABILITY_NOT_FOUND","error_msg","PluginNotFoundError","PLUGIN_NOT_FOUND","INTERNAL_SERVER_ERROR","ActionNotFoundError","ACTION_NOT_FOUND","EXECUTION_ERROR","Error","String","debugStream","res","setHeader","loggerContext","capability_id","plugin_key","log","stringifyForLog","eventStream","callStreamWithEvents","pendingChunk","allChunks","event","type","push","response","delta","content","write","JSON","stringify","finished","end","errorMsg","import_common","WebhookController","logger","Logger","name","capabilityService","list","capabilities","listCapabilities","status_code","ErrorCodes","SUCCESS","data","map","c","id","pluginID","pluginKey","pluginVersion","execute","capabilityId","body","result","load","call","action","params","output","error","CapabilityNotFoundError","HttpException","CAPABILITY_NOT_FOUND","error_msg","HttpStatus","NOT_FOUND","PluginNotFoundError","PLUGIN_NOT_FOUND","INTERNAL_SERVER_ERROR","ActionNotFoundError","ACTION_NOT_FOUND","BAD_REQUEST","EXECUTION_ERROR","Error","message","String","executeStream","res","startTime","Date","now","loggerContext","capability_id","setHeader","log","stringifyForLog","capability","eventStream","callStreamWithEvents","pendingChunk","allChunks","event","type","push","response","delta","content","write","JSON","stringify","finished","length","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","capabilitiesDir","setCapabilitiesDir","inject","RequestContextService","PLATFORM_HTTP_CLIENT","PluginLoaderService","TemplateEngineService","exports"]}
|
|
1
|
+
{"version":3,"sources":["../src/index.ts","../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/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';\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';\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 */\n@Injectable()\nexport class TemplateEngineService {\n // 匹配 {{input.xxx}} 或 {{input.xxx.yyy}} 表达式\n private readonly EXPR_REGEX = /\\{\\{input\\.([a-zA-Z_][a-zA-Z_0-9]*(?:\\.[a-zA-Z_][a-zA-Z_0-9]*)*)\\}\\}/g;\n // 匹配整串单个表达式\n private readonly WHOLE_STRING_EXPR_REGEX = /^\\{\\{input\\.([a-zA-Z_][a-zA-Z_0-9]*(?:\\.[a-zA-Z_][a-zA-Z_0-9]*)*)\\}\\}$/;\n\n resolve(template: unknown, input: Record<string, unknown>): unknown {\n if (typeof template === 'string') {\n return this.resolveString(template, input);\n }\n\n if (Array.isArray(template)) {\n return template.map(item => this.resolve(item, input));\n }\n\n if (template !== null && typeof template === 'object') {\n return this.resolveObject(template as Record<string, unknown>, input);\n }\n\n return template;\n }\n\n private resolveString(template: string, input: Record<string, unknown>): 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 return value !== undefined ? value : template;\n }\n\n // 情况2: 字符串插值 → 返回字符串\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 ): 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);\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","import { Injectable, Logger } from '@nestjs/common';\nimport type { PluginInstance, PluginPackage } from '../interfaces';\n\nexport class PluginNotFoundError extends Error {\n constructor(pluginKey: string) {\n super(`Plugin not found: ${pluginKey}`);\n this.name = 'PluginNotFoundError';\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\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 // eslint-disable-next-line @typescript-eslint/no-require-imports\n const pluginPackage = (await import(pluginKey)).default 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 clearCache(pluginKey?: string): void {\n if (pluginKey) {\n this.pluginInstances.delete(pluginKey);\n this.logger.log(`Cleared cache for plugin: ${pluginKey}`);\n } else {\n this.pluginInstances.clear();\n this.logger.log('Cleared all plugin caches');\n }\n }\n}\n","import { Injectable, Logger, Inject, OnModuleInit } 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 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 constructor(capabilityId: string) {\n super(`Capability not found: ${capabilityId}`);\n this.name = 'CapabilityNotFoundError';\n }\n}\n\nexport class ActionNotFoundError extends Error {\n constructor(pluginKey: string, actionName: string) {\n super(`Action '${actionName}' not found in plugin ${pluginKey}`);\n this.name = 'ActionNotFoundError';\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 capabilitiesDir?: string;\n}\n\n@Injectable()\nexport class CapabilityService implements OnModuleInit {\n private readonly logger = new Logger(CapabilityService.name);\n private readonly capabilities = new Map<string, CapabilityConfig>();\n private capabilitiesDir: string;\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 this.capabilitiesDir = path.join(process.cwd(), 'server/capabilities');\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 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 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: ${file}`);\n continue;\n }\n\n this.capabilities.set(config.id, config);\n this.logger.log(`Loaded capability: ${config.id} (${config.name})`);\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>)\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>)\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>)\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","import {\n Controller,\n Post,\n Get,\n Param,\n Body,\n Res,\n HttpException,\n HttpStatus,\n Logger,\n} from '@nestjs/common';\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@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(): SuccessResponse<ListResponseData> {\n const capabilities = this.capabilityService.listCapabilities();\n\n return {\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 };\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 HttpException(\n {\n code: 1,\n message: `Capability not found: ${capabilityId}`,\n error: 'CAPABILITY_NOT_FOUND',\n },\n HttpStatus.NOT_FOUND,\n );\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 HttpException(\n {\n code: 1,\n message: `Plugin ${pluginKey} has no actions`,\n error: 'NO_ACTIONS',\n },\n HttpStatus.BAD_REQUEST,\n );\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 ): Promise<SuccessResponse<DebugExecuteResponseData> | ErrorResponse> {\n const startTime = Date.now();\n const params = body.params ?? {};\n\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 );\n\n try {\n const result = await this.capabilityService\n .loadWithConfig(config)\n .call(action, params, { isDebug: true });\n\n return {\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 };\n } catch (error) {\n if (error instanceof CapabilityNotFoundError) {\n throw new HttpException(\n {\n status_code: ErrorCodes.CAPABILITY_NOT_FOUND,\n error_msg: `Capability not found: ${capabilityId}`,\n } satisfies ErrorResponse,\n HttpStatus.NOT_FOUND,\n );\n }\n\n if (error instanceof PluginNotFoundError) {\n throw new HttpException(\n {\n status_code: ErrorCodes.PLUGIN_NOT_FOUND,\n error_msg: `Plugin not found: ${config.pluginKey}`,\n } satisfies ErrorResponse,\n HttpStatus.INTERNAL_SERVER_ERROR,\n );\n }\n\n if (error instanceof ActionNotFoundError) {\n throw new HttpException(\n {\n status_code: ErrorCodes.ACTION_NOT_FOUND,\n error_msg: `Action '${action}' not found in plugin ${config.pluginKey}`,\n } satisfies ErrorResponse,\n HttpStatus.BAD_REQUEST,\n );\n }\n\n throw new HttpException(\n {\n status_code: ErrorCodes.EXECUTION_ERROR,\n error_msg: `Execution failed: ${error instanceof Error ? error.message : String(error)}`,\n } satisfies ErrorResponse,\n HttpStatus.INTERNAL_SERVER_ERROR,\n );\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: { content: pendingChunk },\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: { content: pendingChunk },\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: { content: pendingChunk },\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 HttpException,\n HttpStatus,\n Logger,\n} from '@nestjs/common';\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\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(): SuccessResponse<ListResponseData> {\n const capabilities = this.capabilityService.listCapabilities();\n return {\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 };\n }\n\n @Post(':capability_id')\n async execute(\n @Param('capability_id') capabilityId: string,\n @Body() body: ExecuteRequestBody,\n ): Promise<SuccessResponse<ExecuteResponseData> | ErrorResponse> {\n try {\n const result = await this.capabilityService\n .load(capabilityId)\n .call(body.action, body.params);\n\n return {\n status_code: ErrorCodes.SUCCESS,\n data: {\n output: result,\n },\n };\n } catch (error) {\n if (error instanceof CapabilityNotFoundError) {\n throw new HttpException(\n {\n status_code: ErrorCodes.CAPABILITY_NOT_FOUND,\n error_msg: `Capability not found: ${capabilityId}`,\n } satisfies ErrorResponse,\n HttpStatus.NOT_FOUND,\n );\n }\n\n if (error instanceof PluginNotFoundError) {\n throw new HttpException(\n {\n status_code: ErrorCodes.PLUGIN_NOT_FOUND,\n error_msg: `Plugin not found`,\n } satisfies ErrorResponse,\n HttpStatus.INTERNAL_SERVER_ERROR,\n );\n }\n\n if (error instanceof ActionNotFoundError) {\n throw new HttpException(\n {\n status_code: ErrorCodes.ACTION_NOT_FOUND,\n error_msg: `Action '${body.action}' not found`,\n } satisfies ErrorResponse,\n HttpStatus.BAD_REQUEST,\n );\n }\n\n throw new HttpException(\n {\n status_code: ErrorCodes.EXECUTION_ERROR,\n error_msg: `Execution failed: ${error instanceof Error ? error.message : String(error)}`,\n } satisfies ErrorResponse,\n HttpStatus.INTERNAL_SERVER_ERROR,\n );\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: { content: pendingChunk },\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: { content: pendingChunk },\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: { content: pendingChunk },\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 if (moduleOptions?.capabilitiesDir) {\n service.setCapabilitiesDir(moduleOptions.capabilitiesDir);\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":["ErrorCodes","SUCCESS","CAPABILITY_NOT_FOUND","PLUGIN_NOT_FOUND","ACTION_NOT_FOUND","PARAMS_VALIDATION_ERROR","EXECUTION_ERROR","TemplateEngineService","EXPR_REGEX","WHOLE_STRING_EXPR_REGEX","resolve","template","input","resolveString","Array","isArray","map","item","resolveObject","wholeMatch","match","path","value","getValueByPath","undefined","lastIndex","test","result","replace","String","key","Object","entries","obj","keys","split","current","import_common","PluginNotFoundError","Error","pluginKey","name","PluginLoadError","reason","PluginLoaderService","logger","Logger","pluginInstances","Map","loadPlugin","cached","get","debug","log","pluginPackage","default","create","instance","set","error","code","message","String","isPluginInstalled","require","resolve","clearCache","delete","clear","import_common","DEFAULT_MAX_LENGTH","truncateString","str","maxLength","length","slice","stringifyForLog","value","JSON","stringify","String","CapabilityNotFoundError","Error","capabilityId","name","ActionNotFoundError","pluginKey","actionName","CapabilityService","logger","Logger","capabilities","Map","capabilitiesDir","requestContextService","platformHttpClient","pluginLoaderService","templateEngineService","join","process","cwd","setCapabilitiesDir","dir","onModuleInit","loadCapabilities","log","existsSync","warn","files","readdirSync","filter","f","endsWith","file","filePath","content","readFileSync","config","JSON","parse","id","set","error","size","listCapabilities","Array","from","values","getCapability","get","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","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","capabilities","listCapabilities","status_code","ErrorCodes","SUCCESS","data","map","c","id","pluginID","pluginKey","pluginVersion","getCapabilityConfig","capabilityId","bodyCapability","config","getCapability","HttpException","code","message","error","HttpStatus","NOT_FOUND","getActionName","bodyAction","pluginInstance","loadPlugin","actions","listActions","length","BAD_REQUEST","debug","body","startTime","Date","now","params","capability","action","resolvedParams","resolve","formValue","result","loadWithConfig","call","isDebug","output","capabilityConfig","duration","CapabilityNotFoundError","CAPABILITY_NOT_FOUND","error_msg","PluginNotFoundError","PLUGIN_NOT_FOUND","INTERNAL_SERVER_ERROR","ActionNotFoundError","ACTION_NOT_FOUND","EXECUTION_ERROR","Error","String","debugStream","res","setHeader","loggerContext","capability_id","plugin_key","log","input","stringifyForLog","eventStream","callStreamWithEvents","pendingChunk","event","type","response","delta","content","write","JSON","stringify","finished","duration_ms","metadata","aggregated","end","errorMsg","import_common","WebhookController","logger","Logger","name","capabilityService","list","capabilities","listCapabilities","status_code","ErrorCodes","SUCCESS","data","map","c","id","pluginID","pluginKey","pluginVersion","execute","capabilityId","body","result","load","call","action","params","output","error","CapabilityNotFoundError","HttpException","CAPABILITY_NOT_FOUND","error_msg","HttpStatus","NOT_FOUND","PluginNotFoundError","PLUGIN_NOT_FOUND","INTERNAL_SERVER_ERROR","ActionNotFoundError","ACTION_NOT_FOUND","BAD_REQUEST","EXECUTION_ERROR","Error","message","String","executeStream","res","loggerContext","capability_id","setHeader","log","input","stringifyForLog","capability","eventStream","callStreamWithEvents","pendingChunk","event","type","response","delta","content","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","capabilitiesDir","setCapabilitiesDir","inject","RequestContextService","PLATFORM_HTTP_CLIENT","PluginLoaderService","TemplateEngineService","exports"]}
|
package/dist/index.d.cts
CHANGED
package/dist/index.d.ts
CHANGED
package/dist/index.js
CHANGED
|
@@ -461,8 +461,19 @@ var CapabilityService = class _CapabilityService {
|
|
|
461
461
|
for await (const event of pluginInstance.runStreamWithEvents(actionName, context, resolvedParams)) {
|
|
462
462
|
if (event.type === "data") {
|
|
463
463
|
chunks.push(event.data);
|
|
464
|
+
yield event;
|
|
465
|
+
} else if (event.type === "done") {
|
|
466
|
+
const aggregatedResult2 = pluginInstance.aggregate ? pluginInstance.aggregate(actionName, chunks) : chunks;
|
|
467
|
+
yield {
|
|
468
|
+
type: "done",
|
|
469
|
+
metadata: {
|
|
470
|
+
...event.metadata,
|
|
471
|
+
aggregated: aggregatedResult2
|
|
472
|
+
}
|
|
473
|
+
};
|
|
474
|
+
} else {
|
|
475
|
+
yield event;
|
|
464
476
|
}
|
|
465
|
-
yield event;
|
|
466
477
|
}
|
|
467
478
|
} else {
|
|
468
479
|
if (isStream && pluginInstance.runStream) {
|
|
@@ -481,11 +492,13 @@ var CapabilityService = class _CapabilityService {
|
|
|
481
492
|
data: result
|
|
482
493
|
};
|
|
483
494
|
}
|
|
495
|
+
const aggregatedResult2 = pluginInstance.aggregate ? pluginInstance.aggregate(actionName, chunks) : chunks;
|
|
484
496
|
yield {
|
|
485
497
|
type: "done",
|
|
486
498
|
metadata: {
|
|
487
499
|
chunks: chunks.length,
|
|
488
|
-
duration: Date.now() - startTime
|
|
500
|
+
duration: Date.now() - startTime,
|
|
501
|
+
aggregated: aggregatedResult2
|
|
489
502
|
}
|
|
490
503
|
};
|
|
491
504
|
}
|
|
@@ -514,7 +527,8 @@ var CapabilityService = class _CapabilityService {
|
|
|
514
527
|
return {
|
|
515
528
|
logger: this.logger,
|
|
516
529
|
platformHttpClient: this.platformHttpClient,
|
|
517
|
-
userContext: override?.userContext ?? this.getUserContext()
|
|
530
|
+
userContext: override?.userContext ?? this.getUserContext(),
|
|
531
|
+
isDebug: override?.isDebug ?? false
|
|
518
532
|
};
|
|
519
533
|
}
|
|
520
534
|
getUserContext() {
|
|
@@ -628,7 +642,9 @@ var DebugController = class _DebugController {
|
|
|
628
642
|
const action = await this.getActionName(config.pluginKey, body.action);
|
|
629
643
|
const resolvedParams = this.templateEngineService.resolve(config.formValue, params);
|
|
630
644
|
try {
|
|
631
|
-
const result = await this.capabilityService.loadWithConfig(config).call(action, params
|
|
645
|
+
const result = await this.capabilityService.loadWithConfig(config).call(action, params, {
|
|
646
|
+
isDebug: true
|
|
647
|
+
});
|
|
632
648
|
return {
|
|
633
649
|
status_code: ErrorCodes.SUCCESS,
|
|
634
650
|
data: {
|
|
@@ -669,7 +685,6 @@ var DebugController = class _DebugController {
|
|
|
669
685
|
}
|
|
670
686
|
async debugStream(capabilityId, body, res) {
|
|
671
687
|
const params = body.params ?? {};
|
|
672
|
-
const startTime = Date.now();
|
|
673
688
|
res.setHeader("Content-Type", "text/event-stream");
|
|
674
689
|
res.setHeader("Cache-Control", "no-cache");
|
|
675
690
|
res.setHeader("Connection", "keep-alive");
|
|
@@ -681,15 +696,18 @@ var DebugController = class _DebugController {
|
|
|
681
696
|
plugin_key: config.pluginKey,
|
|
682
697
|
action
|
|
683
698
|
};
|
|
684
|
-
this.logger.log(`Executing capability (stream)
|
|
699
|
+
this.logger.log(`Executing capability (stream)`, {
|
|
700
|
+
...loggerContext,
|
|
701
|
+
input: stringifyForLog(params)
|
|
702
|
+
});
|
|
685
703
|
const capability = this.capabilityService.loadWithConfig(config);
|
|
686
|
-
const eventStream = capability.callStreamWithEvents(action, params
|
|
704
|
+
const eventStream = capability.callStreamWithEvents(action, params, {
|
|
705
|
+
isDebug: true
|
|
706
|
+
});
|
|
687
707
|
let pendingChunk = null;
|
|
688
|
-
const allChunks = [];
|
|
689
708
|
for await (const event of eventStream) {
|
|
690
709
|
switch (event.type) {
|
|
691
710
|
case "data": {
|
|
692
|
-
allChunks.push(event.data);
|
|
693
711
|
if (pendingChunk !== null) {
|
|
694
712
|
const response = {
|
|
695
713
|
status_code: ErrorCodes.SUCCESS,
|
|
@@ -723,7 +741,11 @@ var DebugController = class _DebugController {
|
|
|
723
741
|
|
|
724
742
|
`);
|
|
725
743
|
}
|
|
726
|
-
this.logger.log(`Capability (stream) executed successfully
|
|
744
|
+
this.logger.log(`Capability (stream) executed successfully`, {
|
|
745
|
+
...loggerContext,
|
|
746
|
+
duration_ms: event.metadata.duration,
|
|
747
|
+
output: stringifyForLog(event.metadata.aggregated)
|
|
748
|
+
});
|
|
727
749
|
res.end();
|
|
728
750
|
return;
|
|
729
751
|
}
|
|
@@ -898,7 +920,6 @@ var WebhookController = class _WebhookController {
|
|
|
898
920
|
}
|
|
899
921
|
}
|
|
900
922
|
async executeStream(capabilityId, body, res) {
|
|
901
|
-
const startTime = Date.now();
|
|
902
923
|
const loggerContext = {
|
|
903
924
|
capability_id: capabilityId,
|
|
904
925
|
action: body.action
|
|
@@ -907,15 +928,16 @@ var WebhookController = class _WebhookController {
|
|
|
907
928
|
res.setHeader("Cache-Control", "no-cache");
|
|
908
929
|
res.setHeader("Connection", "keep-alive");
|
|
909
930
|
try {
|
|
910
|
-
this.logger.log(`Executing capability (stream)
|
|
931
|
+
this.logger.log(`Executing capability (stream)`, {
|
|
932
|
+
...loggerContext,
|
|
933
|
+
input: stringifyForLog(body.params)
|
|
934
|
+
});
|
|
911
935
|
const capability = this.capabilityService.load(capabilityId);
|
|
912
936
|
const eventStream = capability.callStreamWithEvents(body.action, body.params);
|
|
913
937
|
let pendingChunk = null;
|
|
914
|
-
const allChunks = [];
|
|
915
938
|
for await (const event of eventStream) {
|
|
916
939
|
switch (event.type) {
|
|
917
940
|
case "data": {
|
|
918
|
-
allChunks.push(event.data);
|
|
919
941
|
if (pendingChunk !== null) {
|
|
920
942
|
const response = {
|
|
921
943
|
status_code: ErrorCodes.SUCCESS,
|
|
@@ -949,7 +971,11 @@ var WebhookController = class _WebhookController {
|
|
|
949
971
|
|
|
950
972
|
`);
|
|
951
973
|
}
|
|
952
|
-
this.logger.log(`Capability (stream) executed successfully
|
|
974
|
+
this.logger.log(`Capability (stream) executed successfully`, {
|
|
975
|
+
...loggerContext,
|
|
976
|
+
duration_ms: event.metadata.duration,
|
|
977
|
+
output: stringifyForLog(event.metadata.aggregated)
|
|
978
|
+
});
|
|
953
979
|
res.end();
|
|
954
980
|
return;
|
|
955
981
|
}
|
|
@@ -987,7 +1013,6 @@ var WebhookController = class _WebhookController {
|
|
|
987
1013
|
|
|
988
1014
|
`);
|
|
989
1015
|
}
|
|
990
|
-
this.logger.log(`Capability (stream) executed successfully, duration: ${Date.now() - startTime}ms, chunks: ${allChunks.length}, result: ${stringifyForLog(allChunks)}`, loggerContext);
|
|
991
1016
|
res.end();
|
|
992
1017
|
} catch (error) {
|
|
993
1018
|
const errorMsg = error instanceof Error ? error.message : String(error);
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../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/controllers/debug.controller.ts","../src/controllers/webhook.controller.ts","../src/capability.module.ts"],"sourcesContent":["/**\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';\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 */\n@Injectable()\nexport class TemplateEngineService {\n // 匹配 {{input.xxx}} 或 {{input.xxx.yyy}} 表达式\n private readonly EXPR_REGEX = /\\{\\{input\\.([a-zA-Z_][a-zA-Z_0-9]*(?:\\.[a-zA-Z_][a-zA-Z_0-9]*)*)\\}\\}/g;\n // 匹配整串单个表达式\n private readonly WHOLE_STRING_EXPR_REGEX = /^\\{\\{input\\.([a-zA-Z_][a-zA-Z_0-9]*(?:\\.[a-zA-Z_][a-zA-Z_0-9]*)*)\\}\\}$/;\n\n resolve(template: unknown, input: Record<string, unknown>): unknown {\n if (typeof template === 'string') {\n return this.resolveString(template, input);\n }\n\n if (Array.isArray(template)) {\n return template.map(item => this.resolve(item, input));\n }\n\n if (template !== null && typeof template === 'object') {\n return this.resolveObject(template as Record<string, unknown>, input);\n }\n\n return template;\n }\n\n private resolveString(template: string, input: Record<string, unknown>): 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 return value !== undefined ? value : template;\n }\n\n // 情况2: 字符串插值 → 返回字符串\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 ): 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);\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","import { Injectable, Logger } from '@nestjs/common';\nimport type { PluginInstance, PluginPackage } from '../interfaces';\n\nexport class PluginNotFoundError extends Error {\n constructor(pluginKey: string) {\n super(`Plugin not found: ${pluginKey}`);\n this.name = 'PluginNotFoundError';\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\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 // eslint-disable-next-line @typescript-eslint/no-require-imports\n const pluginPackage = (await import(pluginKey)).default 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 clearCache(pluginKey?: string): void {\n if (pluginKey) {\n this.pluginInstances.delete(pluginKey);\n this.logger.log(`Cleared cache for plugin: ${pluginKey}`);\n } else {\n this.pluginInstances.clear();\n this.logger.log('Cleared all plugin caches');\n }\n }\n}\n","import { Injectable, Logger, Inject, OnModuleInit } 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 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 constructor(capabilityId: string) {\n super(`Capability not found: ${capabilityId}`);\n this.name = 'CapabilityNotFoundError';\n }\n}\n\nexport class ActionNotFoundError extends Error {\n constructor(pluginKey: string, actionName: string) {\n super(`Action '${actionName}' not found in plugin ${pluginKey}`);\n this.name = 'ActionNotFoundError';\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 capabilitiesDir?: string;\n}\n\n@Injectable()\nexport class CapabilityService implements OnModuleInit {\n private readonly logger = new Logger(CapabilityService.name);\n private readonly capabilities = new Map<string, CapabilityConfig>();\n private capabilitiesDir: string;\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 this.capabilitiesDir = path.join(process.cwd(), 'server/capabilities');\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 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 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: ${file}`);\n continue;\n }\n\n this.capabilities.set(config.id, config);\n this.logger.log(`Loaded capability: ${config.id} (${config.name})`);\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>)\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>)\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>)\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 }\n yield event;\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 // 发送 done 事件\n yield {\n type: 'done',\n metadata: {\n chunks: chunks.length,\n duration: Date.now() - startTime,\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 };\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","import {\n Controller,\n Post,\n Get,\n Param,\n Body,\n Res,\n HttpException,\n HttpStatus,\n Logger,\n} from '@nestjs/common';\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@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(): SuccessResponse<ListResponseData> {\n const capabilities = this.capabilityService.listCapabilities();\n\n return {\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 };\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 HttpException(\n {\n code: 1,\n message: `Capability not found: ${capabilityId}`,\n error: 'CAPABILITY_NOT_FOUND',\n },\n HttpStatus.NOT_FOUND,\n );\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 HttpException(\n {\n code: 1,\n message: `Plugin ${pluginKey} has no actions`,\n error: 'NO_ACTIONS',\n },\n HttpStatus.BAD_REQUEST,\n );\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 ): Promise<SuccessResponse<DebugExecuteResponseData> | ErrorResponse> {\n const startTime = Date.now();\n const params = body.params ?? {};\n\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 );\n\n try {\n const result = await this.capabilityService\n .loadWithConfig(config)\n .call(action, params);\n\n return {\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 };\n } catch (error) {\n if (error instanceof CapabilityNotFoundError) {\n throw new HttpException(\n {\n status_code: ErrorCodes.CAPABILITY_NOT_FOUND,\n error_msg: `Capability not found: ${capabilityId}`,\n } satisfies ErrorResponse,\n HttpStatus.NOT_FOUND,\n );\n }\n\n if (error instanceof PluginNotFoundError) {\n throw new HttpException(\n {\n status_code: ErrorCodes.PLUGIN_NOT_FOUND,\n error_msg: `Plugin not found: ${config.pluginKey}`,\n } satisfies ErrorResponse,\n HttpStatus.INTERNAL_SERVER_ERROR,\n );\n }\n\n if (error instanceof ActionNotFoundError) {\n throw new HttpException(\n {\n status_code: ErrorCodes.ACTION_NOT_FOUND,\n error_msg: `Action '${action}' not found in plugin ${config.pluginKey}`,\n } satisfies ErrorResponse,\n HttpStatus.BAD_REQUEST,\n );\n }\n\n throw new HttpException(\n {\n status_code: ErrorCodes.EXECUTION_ERROR,\n error_msg: `Execution failed: ${error instanceof Error ? error.message : String(error)}`,\n } satisfies ErrorResponse,\n HttpStatus.INTERNAL_SERVER_ERROR,\n );\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 const startTime = Date.now();\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), input: ${stringifyForLog(params)}`, loggerContext);\n\n const capability = this.capabilityService.loadWithConfig(config);\n const eventStream = capability.callStreamWithEvents(action, params);\n\n // 延迟发送策略:缓存上一个 chunk,收到下一个 data 或 done 时再发送\n // 这样可以在最后一个 chunk 上标记 finished: true\n let pendingChunk: unknown | null = null;\n // 收集所有 chunk 用于日志\n const allChunks: unknown[] = [];\n\n for await (const event of eventStream) {\n switch (event.type) {\n case 'data': {\n // 收集 chunk 用于日志\n allChunks.push(event.data);\n // 先发送上一个缓存的 chunk(如果有)\n if (pendingChunk !== null) {\n const response: StreamContentResponse = {\n status_code: ErrorCodes.SUCCESS,\n data: {\n type: 'content',\n delta: { content: pendingChunk },\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: { content: pendingChunk },\n finished: true,\n },\n };\n res.write(`data: ${JSON.stringify(response)}\\n\\n`);\n }\n // 打印聚合结果\n this.logger.log(`Capability (stream) executed successfully, duration: ${Date.now() - startTime}ms, chunks: ${allChunks.length}, result: ${stringifyForLog(allChunks)}`, loggerContext);\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: { content: pendingChunk },\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 HttpException,\n HttpStatus,\n Logger,\n} from '@nestjs/common';\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\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(): SuccessResponse<ListResponseData> {\n const capabilities = this.capabilityService.listCapabilities();\n return {\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 };\n }\n\n @Post(':capability_id')\n async execute(\n @Param('capability_id') capabilityId: string,\n @Body() body: ExecuteRequestBody,\n ): Promise<SuccessResponse<ExecuteResponseData> | ErrorResponse> {\n try {\n const result = await this.capabilityService\n .load(capabilityId)\n .call(body.action, body.params);\n\n return {\n status_code: ErrorCodes.SUCCESS,\n data: {\n output: result,\n },\n };\n } catch (error) {\n if (error instanceof CapabilityNotFoundError) {\n throw new HttpException(\n {\n status_code: ErrorCodes.CAPABILITY_NOT_FOUND,\n error_msg: `Capability not found: ${capabilityId}`,\n } satisfies ErrorResponse,\n HttpStatus.NOT_FOUND,\n );\n }\n\n if (error instanceof PluginNotFoundError) {\n throw new HttpException(\n {\n status_code: ErrorCodes.PLUGIN_NOT_FOUND,\n error_msg: `Plugin not found`,\n } satisfies ErrorResponse,\n HttpStatus.INTERNAL_SERVER_ERROR,\n );\n }\n\n if (error instanceof ActionNotFoundError) {\n throw new HttpException(\n {\n status_code: ErrorCodes.ACTION_NOT_FOUND,\n error_msg: `Action '${body.action}' not found`,\n } satisfies ErrorResponse,\n HttpStatus.BAD_REQUEST,\n );\n }\n\n throw new HttpException(\n {\n status_code: ErrorCodes.EXECUTION_ERROR,\n error_msg: `Execution failed: ${error instanceof Error ? error.message : String(error)}`,\n } satisfies ErrorResponse,\n HttpStatus.INTERNAL_SERVER_ERROR,\n );\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 startTime = Date.now();\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), input: ${stringifyForLog(body.params)}`, loggerContext);\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 // 收集所有 chunk 用于日志\n const allChunks: unknown[] = [];\n\n for await (const event of eventStream) {\n switch (event.type) {\n case 'data': {\n // 收集 chunk 用于日志\n allChunks.push(event.data);\n // 先发送上一个缓存的 chunk(如果有)\n if (pendingChunk !== null) {\n const response: StreamContentResponse = {\n status_code: ErrorCodes.SUCCESS,\n data: {\n type: 'content',\n delta: { content: pendingChunk },\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: { content: pendingChunk },\n finished: true,\n },\n };\n res.write(`data: ${JSON.stringify(response)}\\n\\n`);\n }\n // 打印聚合结果\n this.logger.log(`Capability (stream) executed successfully, duration: ${Date.now() - startTime}ms, chunks: ${allChunks.length}, result: ${stringifyForLog(allChunks)}`, loggerContext);\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: { content: pendingChunk },\n finished: true,\n },\n };\n res.write(`data: ${JSON.stringify(response)}\\n\\n`);\n }\n // 打印聚合结果\n this.logger.log(`Capability (stream) executed successfully, duration: ${Date.now() - startTime}ms, chunks: ${allChunks.length}, result: ${stringifyForLog(allChunks)}`, loggerContext);\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 if (moduleOptions?.capabilitiesDir) {\n service.setCapabilitiesDir(moduleOptions.capabilitiesDir);\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":["ErrorCodes","SUCCESS","CAPABILITY_NOT_FOUND","PLUGIN_NOT_FOUND","ACTION_NOT_FOUND","PARAMS_VALIDATION_ERROR","EXECUTION_ERROR","Injectable","TemplateEngineService","EXPR_REGEX","WHOLE_STRING_EXPR_REGEX","resolve","template","input","resolveString","Array","isArray","map","item","resolveObject","wholeMatch","match","path","value","getValueByPath","undefined","lastIndex","test","result","replace","String","key","Object","entries","obj","keys","split","current","Injectable","Logger","PluginNotFoundError","Error","pluginKey","name","PluginLoadError","reason","PluginLoaderService","logger","Logger","pluginInstances","Map","loadPlugin","cached","get","debug","log","pluginPackage","default","create","instance","set","error","code","message","String","isPluginInstalled","require","resolve","clearCache","delete","clear","Injectable","Logger","Inject","RequestContextService","PLATFORM_HTTP_CLIENT","fs","path","DEFAULT_MAX_LENGTH","truncateString","str","maxLength","length","slice","stringifyForLog","value","JSON","stringify","String","CapabilityNotFoundError","Error","capabilityId","name","ActionNotFoundError","pluginKey","actionName","CapabilityService","logger","Logger","capabilities","Map","capabilitiesDir","requestContextService","platformHttpClient","pluginLoaderService","templateEngineService","join","process","cwd","setCapabilitiesDir","dir","onModuleInit","loadCapabilities","log","existsSync","warn","files","readdirSync","filter","f","endsWith","file","filePath","content","readFileSync","config","JSON","parse","id","set","error","size","listCapabilities","Array","from","values","getCapability","get","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","context","buildActionContext","is_stream","stringifyForLog","result","runStream","chunks","chunk","push","aggregate","run","duration_ms","output","message","String","aggregatedResult","runStreamWithEvents","event","type","data","metadata","length","duration","code","override","userContext","getUserContext","ctx","getContext","appId","userId","tenantId","Controller","Post","Get","Param","Body","Res","HttpException","HttpStatus","Logger","DebugController","logger","Logger","name","capabilityService","pluginLoaderService","templateEngineService","list","capabilities","listCapabilities","status_code","ErrorCodes","SUCCESS","data","map","c","id","pluginID","pluginKey","pluginVersion","getCapabilityConfig","capabilityId","bodyCapability","config","getCapability","HttpException","code","message","error","HttpStatus","NOT_FOUND","getActionName","bodyAction","pluginInstance","loadPlugin","actions","listActions","length","BAD_REQUEST","debug","body","startTime","Date","now","params","capability","action","resolvedParams","resolve","formValue","result","loadWithConfig","call","output","capabilityConfig","duration","CapabilityNotFoundError","CAPABILITY_NOT_FOUND","error_msg","PluginNotFoundError","PLUGIN_NOT_FOUND","INTERNAL_SERVER_ERROR","ActionNotFoundError","ACTION_NOT_FOUND","EXECUTION_ERROR","Error","String","debugStream","res","setHeader","loggerContext","capability_id","plugin_key","log","stringifyForLog","eventStream","callStreamWithEvents","pendingChunk","allChunks","event","type","push","response","delta","content","write","JSON","stringify","finished","end","errorMsg","Controller","Get","Post","Param","Body","Res","HttpException","HttpStatus","Logger","WebhookController","logger","Logger","name","capabilityService","list","capabilities","listCapabilities","status_code","ErrorCodes","SUCCESS","data","map","c","id","pluginID","pluginKey","pluginVersion","execute","capabilityId","body","result","load","call","action","params","output","error","CapabilityNotFoundError","HttpException","CAPABILITY_NOT_FOUND","error_msg","HttpStatus","NOT_FOUND","PluginNotFoundError","PLUGIN_NOT_FOUND","INTERNAL_SERVER_ERROR","ActionNotFoundError","ACTION_NOT_FOUND","BAD_REQUEST","EXECUTION_ERROR","Error","message","String","executeStream","res","startTime","Date","now","loggerContext","capability_id","setHeader","log","stringifyForLog","capability","eventStream","callStreamWithEvents","pendingChunk","allChunks","event","type","push","response","delta","content","write","JSON","stringify","finished","length","end","code","errorMsg","Module","CommonModule","RequestContextService","PLATFORM_HTTP_CLIENT","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","capabilitiesDir","setCapabilitiesDir","inject","RequestContextService","PLATFORM_HTTP_CLIENT","PluginLoaderService","TemplateEngineService","exports"]}
|
|
1
|
+
{"version":3,"sources":["../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/controllers/debug.controller.ts","../src/controllers/webhook.controller.ts","../src/capability.module.ts"],"sourcesContent":["/**\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';\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 */\n@Injectable()\nexport class TemplateEngineService {\n // 匹配 {{input.xxx}} 或 {{input.xxx.yyy}} 表达式\n private readonly EXPR_REGEX = /\\{\\{input\\.([a-zA-Z_][a-zA-Z_0-9]*(?:\\.[a-zA-Z_][a-zA-Z_0-9]*)*)\\}\\}/g;\n // 匹配整串单个表达式\n private readonly WHOLE_STRING_EXPR_REGEX = /^\\{\\{input\\.([a-zA-Z_][a-zA-Z_0-9]*(?:\\.[a-zA-Z_][a-zA-Z_0-9]*)*)\\}\\}$/;\n\n resolve(template: unknown, input: Record<string, unknown>): unknown {\n if (typeof template === 'string') {\n return this.resolveString(template, input);\n }\n\n if (Array.isArray(template)) {\n return template.map(item => this.resolve(item, input));\n }\n\n if (template !== null && typeof template === 'object') {\n return this.resolveObject(template as Record<string, unknown>, input);\n }\n\n return template;\n }\n\n private resolveString(template: string, input: Record<string, unknown>): 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 return value !== undefined ? value : template;\n }\n\n // 情况2: 字符串插值 → 返回字符串\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 ): 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);\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","import { Injectable, Logger } from '@nestjs/common';\nimport type { PluginInstance, PluginPackage } from '../interfaces';\n\nexport class PluginNotFoundError extends Error {\n constructor(pluginKey: string) {\n super(`Plugin not found: ${pluginKey}`);\n this.name = 'PluginNotFoundError';\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\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 // eslint-disable-next-line @typescript-eslint/no-require-imports\n const pluginPackage = (await import(pluginKey)).default 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 clearCache(pluginKey?: string): void {\n if (pluginKey) {\n this.pluginInstances.delete(pluginKey);\n this.logger.log(`Cleared cache for plugin: ${pluginKey}`);\n } else {\n this.pluginInstances.clear();\n this.logger.log('Cleared all plugin caches');\n }\n }\n}\n","import { Injectable, Logger, Inject, OnModuleInit } 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 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 constructor(capabilityId: string) {\n super(`Capability not found: ${capabilityId}`);\n this.name = 'CapabilityNotFoundError';\n }\n}\n\nexport class ActionNotFoundError extends Error {\n constructor(pluginKey: string, actionName: string) {\n super(`Action '${actionName}' not found in plugin ${pluginKey}`);\n this.name = 'ActionNotFoundError';\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 capabilitiesDir?: string;\n}\n\n@Injectable()\nexport class CapabilityService implements OnModuleInit {\n private readonly logger = new Logger(CapabilityService.name);\n private readonly capabilities = new Map<string, CapabilityConfig>();\n private capabilitiesDir: string;\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 this.capabilitiesDir = path.join(process.cwd(), 'server/capabilities');\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 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 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: ${file}`);\n continue;\n }\n\n this.capabilities.set(config.id, config);\n this.logger.log(`Loaded capability: ${config.id} (${config.name})`);\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>)\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>)\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>)\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","import {\n Controller,\n Post,\n Get,\n Param,\n Body,\n Res,\n HttpException,\n HttpStatus,\n Logger,\n} from '@nestjs/common';\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@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(): SuccessResponse<ListResponseData> {\n const capabilities = this.capabilityService.listCapabilities();\n\n return {\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 };\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 HttpException(\n {\n code: 1,\n message: `Capability not found: ${capabilityId}`,\n error: 'CAPABILITY_NOT_FOUND',\n },\n HttpStatus.NOT_FOUND,\n );\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 HttpException(\n {\n code: 1,\n message: `Plugin ${pluginKey} has no actions`,\n error: 'NO_ACTIONS',\n },\n HttpStatus.BAD_REQUEST,\n );\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 ): Promise<SuccessResponse<DebugExecuteResponseData> | ErrorResponse> {\n const startTime = Date.now();\n const params = body.params ?? {};\n\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 );\n\n try {\n const result = await this.capabilityService\n .loadWithConfig(config)\n .call(action, params, { isDebug: true });\n\n return {\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 };\n } catch (error) {\n if (error instanceof CapabilityNotFoundError) {\n throw new HttpException(\n {\n status_code: ErrorCodes.CAPABILITY_NOT_FOUND,\n error_msg: `Capability not found: ${capabilityId}`,\n } satisfies ErrorResponse,\n HttpStatus.NOT_FOUND,\n );\n }\n\n if (error instanceof PluginNotFoundError) {\n throw new HttpException(\n {\n status_code: ErrorCodes.PLUGIN_NOT_FOUND,\n error_msg: `Plugin not found: ${config.pluginKey}`,\n } satisfies ErrorResponse,\n HttpStatus.INTERNAL_SERVER_ERROR,\n );\n }\n\n if (error instanceof ActionNotFoundError) {\n throw new HttpException(\n {\n status_code: ErrorCodes.ACTION_NOT_FOUND,\n error_msg: `Action '${action}' not found in plugin ${config.pluginKey}`,\n } satisfies ErrorResponse,\n HttpStatus.BAD_REQUEST,\n );\n }\n\n throw new HttpException(\n {\n status_code: ErrorCodes.EXECUTION_ERROR,\n error_msg: `Execution failed: ${error instanceof Error ? error.message : String(error)}`,\n } satisfies ErrorResponse,\n HttpStatus.INTERNAL_SERVER_ERROR,\n );\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: { content: pendingChunk },\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: { content: pendingChunk },\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: { content: pendingChunk },\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 HttpException,\n HttpStatus,\n Logger,\n} from '@nestjs/common';\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\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(): SuccessResponse<ListResponseData> {\n const capabilities = this.capabilityService.listCapabilities();\n return {\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 };\n }\n\n @Post(':capability_id')\n async execute(\n @Param('capability_id') capabilityId: string,\n @Body() body: ExecuteRequestBody,\n ): Promise<SuccessResponse<ExecuteResponseData> | ErrorResponse> {\n try {\n const result = await this.capabilityService\n .load(capabilityId)\n .call(body.action, body.params);\n\n return {\n status_code: ErrorCodes.SUCCESS,\n data: {\n output: result,\n },\n };\n } catch (error) {\n if (error instanceof CapabilityNotFoundError) {\n throw new HttpException(\n {\n status_code: ErrorCodes.CAPABILITY_NOT_FOUND,\n error_msg: `Capability not found: ${capabilityId}`,\n } satisfies ErrorResponse,\n HttpStatus.NOT_FOUND,\n );\n }\n\n if (error instanceof PluginNotFoundError) {\n throw new HttpException(\n {\n status_code: ErrorCodes.PLUGIN_NOT_FOUND,\n error_msg: `Plugin not found`,\n } satisfies ErrorResponse,\n HttpStatus.INTERNAL_SERVER_ERROR,\n );\n }\n\n if (error instanceof ActionNotFoundError) {\n throw new HttpException(\n {\n status_code: ErrorCodes.ACTION_NOT_FOUND,\n error_msg: `Action '${body.action}' not found`,\n } satisfies ErrorResponse,\n HttpStatus.BAD_REQUEST,\n );\n }\n\n throw new HttpException(\n {\n status_code: ErrorCodes.EXECUTION_ERROR,\n error_msg: `Execution failed: ${error instanceof Error ? error.message : String(error)}`,\n } satisfies ErrorResponse,\n HttpStatus.INTERNAL_SERVER_ERROR,\n );\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: { content: pendingChunk },\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: { content: pendingChunk },\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: { content: pendingChunk },\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 if (moduleOptions?.capabilitiesDir) {\n service.setCapabilitiesDir(moduleOptions.capabilitiesDir);\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":["ErrorCodes","SUCCESS","CAPABILITY_NOT_FOUND","PLUGIN_NOT_FOUND","ACTION_NOT_FOUND","PARAMS_VALIDATION_ERROR","EXECUTION_ERROR","Injectable","TemplateEngineService","EXPR_REGEX","WHOLE_STRING_EXPR_REGEX","resolve","template","input","resolveString","Array","isArray","map","item","resolveObject","wholeMatch","match","path","value","getValueByPath","undefined","lastIndex","test","result","replace","String","key","Object","entries","obj","keys","split","current","Injectable","Logger","PluginNotFoundError","Error","pluginKey","name","PluginLoadError","reason","PluginLoaderService","logger","Logger","pluginInstances","Map","loadPlugin","cached","get","debug","log","pluginPackage","default","create","instance","set","error","code","message","String","isPluginInstalled","require","resolve","clearCache","delete","clear","Injectable","Logger","Inject","RequestContextService","PLATFORM_HTTP_CLIENT","fs","path","DEFAULT_MAX_LENGTH","truncateString","str","maxLength","length","slice","stringifyForLog","value","JSON","stringify","String","CapabilityNotFoundError","Error","capabilityId","name","ActionNotFoundError","pluginKey","actionName","CapabilityService","logger","Logger","capabilities","Map","capabilitiesDir","requestContextService","platformHttpClient","pluginLoaderService","templateEngineService","join","process","cwd","setCapabilitiesDir","dir","onModuleInit","loadCapabilities","log","existsSync","warn","files","readdirSync","filter","f","endsWith","file","filePath","content","readFileSync","config","JSON","parse","id","set","error","size","listCapabilities","Array","from","values","getCapability","get","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","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","Controller","Post","Get","Param","Body","Res","HttpException","HttpStatus","Logger","DebugController","logger","Logger","name","capabilityService","pluginLoaderService","templateEngineService","list","capabilities","listCapabilities","status_code","ErrorCodes","SUCCESS","data","map","c","id","pluginID","pluginKey","pluginVersion","getCapabilityConfig","capabilityId","bodyCapability","config","getCapability","HttpException","code","message","error","HttpStatus","NOT_FOUND","getActionName","bodyAction","pluginInstance","loadPlugin","actions","listActions","length","BAD_REQUEST","debug","body","startTime","Date","now","params","capability","action","resolvedParams","resolve","formValue","result","loadWithConfig","call","isDebug","output","capabilityConfig","duration","CapabilityNotFoundError","CAPABILITY_NOT_FOUND","error_msg","PluginNotFoundError","PLUGIN_NOT_FOUND","INTERNAL_SERVER_ERROR","ActionNotFoundError","ACTION_NOT_FOUND","EXECUTION_ERROR","Error","String","debugStream","res","setHeader","loggerContext","capability_id","plugin_key","log","input","stringifyForLog","eventStream","callStreamWithEvents","pendingChunk","event","type","response","delta","content","write","JSON","stringify","finished","duration_ms","metadata","aggregated","end","errorMsg","Controller","Get","Post","Param","Body","Res","HttpException","HttpStatus","Logger","WebhookController","logger","Logger","name","capabilityService","list","capabilities","listCapabilities","status_code","ErrorCodes","SUCCESS","data","map","c","id","pluginID","pluginKey","pluginVersion","execute","capabilityId","body","result","load","call","action","params","output","error","CapabilityNotFoundError","HttpException","CAPABILITY_NOT_FOUND","error_msg","HttpStatus","NOT_FOUND","PluginNotFoundError","PLUGIN_NOT_FOUND","INTERNAL_SERVER_ERROR","ActionNotFoundError","ACTION_NOT_FOUND","BAD_REQUEST","EXECUTION_ERROR","Error","message","String","executeStream","res","loggerContext","capability_id","setHeader","log","input","stringifyForLog","capability","eventStream","callStreamWithEvents","pendingChunk","event","type","response","delta","content","write","JSON","stringify","finished","duration_ms","metadata","duration","aggregated","end","code","errorMsg","Module","CommonModule","RequestContextService","PLATFORM_HTTP_CLIENT","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","capabilitiesDir","setCapabilitiesDir","inject","RequestContextService","PLATFORM_HTTP_CLIENT","PluginLoaderService","TemplateEngineService","exports"]}
|