@lark-apaas/nestjs-capability 0.0.1-alpha.7 → 0.0.1-alpha.9
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 +43 -17
- 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 +43 -17
- 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
|
@@ -290,7 +290,8 @@ var CapabilityService = class _CapabilityService {
|
|
|
290
290
|
this.platformHttpClient = platformHttpClient;
|
|
291
291
|
this.pluginLoaderService = pluginLoaderService;
|
|
292
292
|
this.templateEngineService = templateEngineService;
|
|
293
|
-
|
|
293
|
+
const isDev = process.env.NODE_ENV === "development";
|
|
294
|
+
this.capabilitiesDir = path.join(process.cwd(), isDev ? "server/capabilities" : "capabilities");
|
|
294
295
|
}
|
|
295
296
|
setCapabilitiesDir(dir) {
|
|
296
297
|
this.capabilitiesDir = dir;
|
|
@@ -499,8 +500,19 @@ var CapabilityService = class _CapabilityService {
|
|
|
499
500
|
for await (const event of pluginInstance.runStreamWithEvents(actionName, context, resolvedParams)) {
|
|
500
501
|
if (event.type === "data") {
|
|
501
502
|
chunks.push(event.data);
|
|
503
|
+
yield event;
|
|
504
|
+
} else if (event.type === "done") {
|
|
505
|
+
const aggregatedResult2 = pluginInstance.aggregate ? pluginInstance.aggregate(actionName, chunks) : chunks;
|
|
506
|
+
yield {
|
|
507
|
+
type: "done",
|
|
508
|
+
metadata: {
|
|
509
|
+
...event.metadata,
|
|
510
|
+
aggregated: aggregatedResult2
|
|
511
|
+
}
|
|
512
|
+
};
|
|
513
|
+
} else {
|
|
514
|
+
yield event;
|
|
502
515
|
}
|
|
503
|
-
yield event;
|
|
504
516
|
}
|
|
505
517
|
} else {
|
|
506
518
|
if (isStream && pluginInstance.runStream) {
|
|
@@ -519,11 +531,13 @@ var CapabilityService = class _CapabilityService {
|
|
|
519
531
|
data: result
|
|
520
532
|
};
|
|
521
533
|
}
|
|
534
|
+
const aggregatedResult2 = pluginInstance.aggregate ? pluginInstance.aggregate(actionName, chunks) : chunks;
|
|
522
535
|
yield {
|
|
523
536
|
type: "done",
|
|
524
537
|
metadata: {
|
|
525
538
|
chunks: chunks.length,
|
|
526
|
-
duration: Date.now() - startTime
|
|
539
|
+
duration: Date.now() - startTime,
|
|
540
|
+
aggregated: aggregatedResult2
|
|
527
541
|
}
|
|
528
542
|
};
|
|
529
543
|
}
|
|
@@ -552,7 +566,8 @@ var CapabilityService = class _CapabilityService {
|
|
|
552
566
|
return {
|
|
553
567
|
logger: this.logger,
|
|
554
568
|
platformHttpClient: this.platformHttpClient,
|
|
555
|
-
userContext: override?.userContext ?? this.getUserContext()
|
|
569
|
+
userContext: override?.userContext ?? this.getUserContext(),
|
|
570
|
+
isDebug: override?.isDebug ?? false
|
|
556
571
|
};
|
|
557
572
|
}
|
|
558
573
|
getUserContext() {
|
|
@@ -666,7 +681,9 @@ var DebugController = class _DebugController {
|
|
|
666
681
|
const action = await this.getActionName(config.pluginKey, body.action);
|
|
667
682
|
const resolvedParams = this.templateEngineService.resolve(config.formValue, params);
|
|
668
683
|
try {
|
|
669
|
-
const result = await this.capabilityService.loadWithConfig(config).call(action, params
|
|
684
|
+
const result = await this.capabilityService.loadWithConfig(config).call(action, params, {
|
|
685
|
+
isDebug: true
|
|
686
|
+
});
|
|
670
687
|
return {
|
|
671
688
|
status_code: ErrorCodes.SUCCESS,
|
|
672
689
|
data: {
|
|
@@ -707,7 +724,6 @@ var DebugController = class _DebugController {
|
|
|
707
724
|
}
|
|
708
725
|
async debugStream(capabilityId, body, res) {
|
|
709
726
|
const params = body.params ?? {};
|
|
710
|
-
const startTime = Date.now();
|
|
711
727
|
res.setHeader("Content-Type", "text/event-stream");
|
|
712
728
|
res.setHeader("Cache-Control", "no-cache");
|
|
713
729
|
res.setHeader("Connection", "keep-alive");
|
|
@@ -719,15 +735,18 @@ var DebugController = class _DebugController {
|
|
|
719
735
|
plugin_key: config.pluginKey,
|
|
720
736
|
action
|
|
721
737
|
};
|
|
722
|
-
this.logger.log(`Executing capability (stream)
|
|
738
|
+
this.logger.log(`Executing capability (stream)`, {
|
|
739
|
+
...loggerContext,
|
|
740
|
+
input: stringifyForLog(params)
|
|
741
|
+
});
|
|
723
742
|
const capability = this.capabilityService.loadWithConfig(config);
|
|
724
|
-
const eventStream = capability.callStreamWithEvents(action, params
|
|
743
|
+
const eventStream = capability.callStreamWithEvents(action, params, {
|
|
744
|
+
isDebug: true
|
|
745
|
+
});
|
|
725
746
|
let pendingChunk = null;
|
|
726
|
-
const allChunks = [];
|
|
727
747
|
for await (const event of eventStream) {
|
|
728
748
|
switch (event.type) {
|
|
729
749
|
case "data": {
|
|
730
|
-
allChunks.push(event.data);
|
|
731
750
|
if (pendingChunk !== null) {
|
|
732
751
|
const response = {
|
|
733
752
|
status_code: ErrorCodes.SUCCESS,
|
|
@@ -761,7 +780,11 @@ var DebugController = class _DebugController {
|
|
|
761
780
|
|
|
762
781
|
`);
|
|
763
782
|
}
|
|
764
|
-
this.logger.log(`Capability (stream) executed successfully
|
|
783
|
+
this.logger.log(`Capability (stream) executed successfully`, {
|
|
784
|
+
...loggerContext,
|
|
785
|
+
duration_ms: event.metadata.duration,
|
|
786
|
+
output: stringifyForLog(event.metadata.aggregated)
|
|
787
|
+
});
|
|
765
788
|
res.end();
|
|
766
789
|
return;
|
|
767
790
|
}
|
|
@@ -936,7 +959,6 @@ var WebhookController = class _WebhookController {
|
|
|
936
959
|
}
|
|
937
960
|
}
|
|
938
961
|
async executeStream(capabilityId, body, res) {
|
|
939
|
-
const startTime = Date.now();
|
|
940
962
|
const loggerContext = {
|
|
941
963
|
capability_id: capabilityId,
|
|
942
964
|
action: body.action
|
|
@@ -945,15 +967,16 @@ var WebhookController = class _WebhookController {
|
|
|
945
967
|
res.setHeader("Cache-Control", "no-cache");
|
|
946
968
|
res.setHeader("Connection", "keep-alive");
|
|
947
969
|
try {
|
|
948
|
-
this.logger.log(`Executing capability (stream)
|
|
970
|
+
this.logger.log(`Executing capability (stream)`, {
|
|
971
|
+
...loggerContext,
|
|
972
|
+
input: stringifyForLog(body.params)
|
|
973
|
+
});
|
|
949
974
|
const capability = this.capabilityService.load(capabilityId);
|
|
950
975
|
const eventStream = capability.callStreamWithEvents(body.action, body.params);
|
|
951
976
|
let pendingChunk = null;
|
|
952
|
-
const allChunks = [];
|
|
953
977
|
for await (const event of eventStream) {
|
|
954
978
|
switch (event.type) {
|
|
955
979
|
case "data": {
|
|
956
|
-
allChunks.push(event.data);
|
|
957
980
|
if (pendingChunk !== null) {
|
|
958
981
|
const response = {
|
|
959
982
|
status_code: ErrorCodes.SUCCESS,
|
|
@@ -987,7 +1010,11 @@ var WebhookController = class _WebhookController {
|
|
|
987
1010
|
|
|
988
1011
|
`);
|
|
989
1012
|
}
|
|
990
|
-
this.logger.log(`Capability (stream) executed successfully
|
|
1013
|
+
this.logger.log(`Capability (stream) executed successfully`, {
|
|
1014
|
+
...loggerContext,
|
|
1015
|
+
duration_ms: event.metadata.duration,
|
|
1016
|
+
output: stringifyForLog(event.metadata.aggregated)
|
|
1017
|
+
});
|
|
991
1018
|
res.end();
|
|
992
1019
|
return;
|
|
993
1020
|
}
|
|
@@ -1025,7 +1052,6 @@ var WebhookController = class _WebhookController {
|
|
|
1025
1052
|
|
|
1026
1053
|
`);
|
|
1027
1054
|
}
|
|
1028
|
-
this.logger.log(`Capability (stream) executed successfully, duration: ${Date.now() - startTime}ms, chunks: ${allChunks.length}, result: ${stringifyForLog(allChunks)}`, loggerContext);
|
|
1029
1055
|
res.end();
|
|
1030
1056
|
} catch (error) {
|
|
1031
1057
|
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 const isDev = process.env.NODE_ENV === 'development';\n this.capabilitiesDir = path.join(process.cwd(), isDev ? 'server/capabilities' : '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","isDev","process","env","NODE_ENV","join","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
|
@@ -252,7 +252,8 @@ var CapabilityService = class _CapabilityService {
|
|
|
252
252
|
this.platformHttpClient = platformHttpClient;
|
|
253
253
|
this.pluginLoaderService = pluginLoaderService;
|
|
254
254
|
this.templateEngineService = templateEngineService;
|
|
255
|
-
|
|
255
|
+
const isDev = process.env.NODE_ENV === "development";
|
|
256
|
+
this.capabilitiesDir = path.join(process.cwd(), isDev ? "server/capabilities" : "capabilities");
|
|
256
257
|
}
|
|
257
258
|
setCapabilitiesDir(dir) {
|
|
258
259
|
this.capabilitiesDir = dir;
|
|
@@ -461,8 +462,19 @@ var CapabilityService = class _CapabilityService {
|
|
|
461
462
|
for await (const event of pluginInstance.runStreamWithEvents(actionName, context, resolvedParams)) {
|
|
462
463
|
if (event.type === "data") {
|
|
463
464
|
chunks.push(event.data);
|
|
465
|
+
yield event;
|
|
466
|
+
} else if (event.type === "done") {
|
|
467
|
+
const aggregatedResult2 = pluginInstance.aggregate ? pluginInstance.aggregate(actionName, chunks) : chunks;
|
|
468
|
+
yield {
|
|
469
|
+
type: "done",
|
|
470
|
+
metadata: {
|
|
471
|
+
...event.metadata,
|
|
472
|
+
aggregated: aggregatedResult2
|
|
473
|
+
}
|
|
474
|
+
};
|
|
475
|
+
} else {
|
|
476
|
+
yield event;
|
|
464
477
|
}
|
|
465
|
-
yield event;
|
|
466
478
|
}
|
|
467
479
|
} else {
|
|
468
480
|
if (isStream && pluginInstance.runStream) {
|
|
@@ -481,11 +493,13 @@ var CapabilityService = class _CapabilityService {
|
|
|
481
493
|
data: result
|
|
482
494
|
};
|
|
483
495
|
}
|
|
496
|
+
const aggregatedResult2 = pluginInstance.aggregate ? pluginInstance.aggregate(actionName, chunks) : chunks;
|
|
484
497
|
yield {
|
|
485
498
|
type: "done",
|
|
486
499
|
metadata: {
|
|
487
500
|
chunks: chunks.length,
|
|
488
|
-
duration: Date.now() - startTime
|
|
501
|
+
duration: Date.now() - startTime,
|
|
502
|
+
aggregated: aggregatedResult2
|
|
489
503
|
}
|
|
490
504
|
};
|
|
491
505
|
}
|
|
@@ -514,7 +528,8 @@ var CapabilityService = class _CapabilityService {
|
|
|
514
528
|
return {
|
|
515
529
|
logger: this.logger,
|
|
516
530
|
platformHttpClient: this.platformHttpClient,
|
|
517
|
-
userContext: override?.userContext ?? this.getUserContext()
|
|
531
|
+
userContext: override?.userContext ?? this.getUserContext(),
|
|
532
|
+
isDebug: override?.isDebug ?? false
|
|
518
533
|
};
|
|
519
534
|
}
|
|
520
535
|
getUserContext() {
|
|
@@ -628,7 +643,9 @@ var DebugController = class _DebugController {
|
|
|
628
643
|
const action = await this.getActionName(config.pluginKey, body.action);
|
|
629
644
|
const resolvedParams = this.templateEngineService.resolve(config.formValue, params);
|
|
630
645
|
try {
|
|
631
|
-
const result = await this.capabilityService.loadWithConfig(config).call(action, params
|
|
646
|
+
const result = await this.capabilityService.loadWithConfig(config).call(action, params, {
|
|
647
|
+
isDebug: true
|
|
648
|
+
});
|
|
632
649
|
return {
|
|
633
650
|
status_code: ErrorCodes.SUCCESS,
|
|
634
651
|
data: {
|
|
@@ -669,7 +686,6 @@ var DebugController = class _DebugController {
|
|
|
669
686
|
}
|
|
670
687
|
async debugStream(capabilityId, body, res) {
|
|
671
688
|
const params = body.params ?? {};
|
|
672
|
-
const startTime = Date.now();
|
|
673
689
|
res.setHeader("Content-Type", "text/event-stream");
|
|
674
690
|
res.setHeader("Cache-Control", "no-cache");
|
|
675
691
|
res.setHeader("Connection", "keep-alive");
|
|
@@ -681,15 +697,18 @@ var DebugController = class _DebugController {
|
|
|
681
697
|
plugin_key: config.pluginKey,
|
|
682
698
|
action
|
|
683
699
|
};
|
|
684
|
-
this.logger.log(`Executing capability (stream)
|
|
700
|
+
this.logger.log(`Executing capability (stream)`, {
|
|
701
|
+
...loggerContext,
|
|
702
|
+
input: stringifyForLog(params)
|
|
703
|
+
});
|
|
685
704
|
const capability = this.capabilityService.loadWithConfig(config);
|
|
686
|
-
const eventStream = capability.callStreamWithEvents(action, params
|
|
705
|
+
const eventStream = capability.callStreamWithEvents(action, params, {
|
|
706
|
+
isDebug: true
|
|
707
|
+
});
|
|
687
708
|
let pendingChunk = null;
|
|
688
|
-
const allChunks = [];
|
|
689
709
|
for await (const event of eventStream) {
|
|
690
710
|
switch (event.type) {
|
|
691
711
|
case "data": {
|
|
692
|
-
allChunks.push(event.data);
|
|
693
712
|
if (pendingChunk !== null) {
|
|
694
713
|
const response = {
|
|
695
714
|
status_code: ErrorCodes.SUCCESS,
|
|
@@ -723,7 +742,11 @@ var DebugController = class _DebugController {
|
|
|
723
742
|
|
|
724
743
|
`);
|
|
725
744
|
}
|
|
726
|
-
this.logger.log(`Capability (stream) executed successfully
|
|
745
|
+
this.logger.log(`Capability (stream) executed successfully`, {
|
|
746
|
+
...loggerContext,
|
|
747
|
+
duration_ms: event.metadata.duration,
|
|
748
|
+
output: stringifyForLog(event.metadata.aggregated)
|
|
749
|
+
});
|
|
727
750
|
res.end();
|
|
728
751
|
return;
|
|
729
752
|
}
|
|
@@ -898,7 +921,6 @@ var WebhookController = class _WebhookController {
|
|
|
898
921
|
}
|
|
899
922
|
}
|
|
900
923
|
async executeStream(capabilityId, body, res) {
|
|
901
|
-
const startTime = Date.now();
|
|
902
924
|
const loggerContext = {
|
|
903
925
|
capability_id: capabilityId,
|
|
904
926
|
action: body.action
|
|
@@ -907,15 +929,16 @@ var WebhookController = class _WebhookController {
|
|
|
907
929
|
res.setHeader("Cache-Control", "no-cache");
|
|
908
930
|
res.setHeader("Connection", "keep-alive");
|
|
909
931
|
try {
|
|
910
|
-
this.logger.log(`Executing capability (stream)
|
|
932
|
+
this.logger.log(`Executing capability (stream)`, {
|
|
933
|
+
...loggerContext,
|
|
934
|
+
input: stringifyForLog(body.params)
|
|
935
|
+
});
|
|
911
936
|
const capability = this.capabilityService.load(capabilityId);
|
|
912
937
|
const eventStream = capability.callStreamWithEvents(body.action, body.params);
|
|
913
938
|
let pendingChunk = null;
|
|
914
|
-
const allChunks = [];
|
|
915
939
|
for await (const event of eventStream) {
|
|
916
940
|
switch (event.type) {
|
|
917
941
|
case "data": {
|
|
918
|
-
allChunks.push(event.data);
|
|
919
942
|
if (pendingChunk !== null) {
|
|
920
943
|
const response = {
|
|
921
944
|
status_code: ErrorCodes.SUCCESS,
|
|
@@ -949,7 +972,11 @@ var WebhookController = class _WebhookController {
|
|
|
949
972
|
|
|
950
973
|
`);
|
|
951
974
|
}
|
|
952
|
-
this.logger.log(`Capability (stream) executed successfully
|
|
975
|
+
this.logger.log(`Capability (stream) executed successfully`, {
|
|
976
|
+
...loggerContext,
|
|
977
|
+
duration_ms: event.metadata.duration,
|
|
978
|
+
output: stringifyForLog(event.metadata.aggregated)
|
|
979
|
+
});
|
|
953
980
|
res.end();
|
|
954
981
|
return;
|
|
955
982
|
}
|
|
@@ -987,7 +1014,6 @@ var WebhookController = class _WebhookController {
|
|
|
987
1014
|
|
|
988
1015
|
`);
|
|
989
1016
|
}
|
|
990
|
-
this.logger.log(`Capability (stream) executed successfully, duration: ${Date.now() - startTime}ms, chunks: ${allChunks.length}, result: ${stringifyForLog(allChunks)}`, loggerContext);
|
|
991
1017
|
res.end();
|
|
992
1018
|
} catch (error) {
|
|
993
1019
|
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 const isDev = process.env.NODE_ENV === 'development';\n this.capabilitiesDir = path.join(process.cwd(), isDev ? 'server/capabilities' : '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","isDev","process","env","NODE_ENV","join","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"]}
|