@lark-apaas/nestjs-capability 0.0.1-alpha.1 → 0.0.1-alpha.11

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/dist/index.js CHANGED
@@ -1,11 +1,21 @@
1
1
  var __defProp = Object.defineProperty;
2
2
  var __name = (target, value) => __defProp(target, "name", { value, configurable: true });
3
- var __require = /* @__PURE__ */ ((x) => typeof require !== "undefined" ? require : typeof Proxy !== "undefined" ? new Proxy(x, {
4
- get: (a, b) => (typeof require !== "undefined" ? require : a)[b]
5
- }) : x)(function(x) {
6
- if (typeof require !== "undefined") return require.apply(this, arguments);
7
- throw Error('Dynamic require of "' + x + '" is not supported');
8
- });
3
+
4
+ // src/interfaces/error-codes.ts
5
+ var ErrorCodes = {
6
+ /** 成功 */
7
+ SUCCESS: "0",
8
+ /** 能力不存在 */
9
+ CAPABILITY_NOT_FOUND: "k_ec_cap_001",
10
+ /** 插件不存在 */
11
+ PLUGIN_NOT_FOUND: "k_ec_cap_002",
12
+ /** Action 不存在 */
13
+ ACTION_NOT_FOUND: "k_ec_cap_003",
14
+ /** 参数验证失败 */
15
+ PARAMS_VALIDATION_ERROR: "k_ec_cap_004",
16
+ /** 执行失败 */
17
+ EXECUTION_ERROR: "k_ec_cap_005"
18
+ };
9
19
 
10
20
  // src/services/template-engine.service.ts
11
21
  import { Injectable } from "@nestjs/common";
@@ -20,7 +30,10 @@ var TemplateEngineService = class {
20
30
  static {
21
31
  __name(this, "TemplateEngineService");
22
32
  }
23
- TEMPLATE_REGEX = /^\{\{input\.(.+)\}\}$/;
33
+ // 匹配 {{input.xxx}} 或 {{input.xxx.yyy}} 表达式
34
+ EXPR_REGEX = /\{\{input\.([a-zA-Z_][a-zA-Z_0-9]*(?:\.[a-zA-Z_][a-zA-Z_0-9]*)*)\}\}/g;
35
+ // 匹配整串单个表达式
36
+ WHOLE_STRING_EXPR_REGEX = /^\{\{input\.([a-zA-Z_][a-zA-Z_0-9]*(?:\.[a-zA-Z_][a-zA-Z_0-9]*)*)\}\}$/;
24
37
  resolve(template, input) {
25
38
  if (typeof template === "string") {
26
39
  return this.resolveString(template, input);
@@ -34,12 +47,25 @@ var TemplateEngineService = class {
34
47
  return template;
35
48
  }
36
49
  resolveString(template, input) {
37
- const match = template.match(this.TEMPLATE_REGEX);
38
- if (!match) {
50
+ const wholeMatch = template.match(this.WHOLE_STRING_EXPR_REGEX);
51
+ if (wholeMatch) {
52
+ const path2 = wholeMatch[1];
53
+ const value = this.getValueByPath(input, path2);
54
+ return value !== void 0 ? value : template;
55
+ }
56
+ this.EXPR_REGEX.lastIndex = 0;
57
+ if (!this.EXPR_REGEX.test(template)) {
39
58
  return template;
40
59
  }
41
- const path2 = match[1];
42
- return this.getValueByPath(input, path2);
60
+ this.EXPR_REGEX.lastIndex = 0;
61
+ const result = template.replace(this.EXPR_REGEX, (match, path2) => {
62
+ const value = this.getValueByPath(input, path2);
63
+ if (value === void 0) {
64
+ return match;
65
+ }
66
+ return String(value);
67
+ });
68
+ return result;
43
69
  }
44
70
  resolveObject(template, input) {
45
71
  const result = {};
@@ -66,6 +92,8 @@ TemplateEngineService = _ts_decorate([
66
92
 
67
93
  // src/services/plugin-loader.service.ts
68
94
  import { Injectable as Injectable2, Logger } from "@nestjs/common";
95
+ import { createRequire } from "module";
96
+ import { pathToFileURL } from "url";
69
97
  function _ts_decorate2(decorators, target, key, desc) {
70
98
  var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
71
99
  if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
@@ -73,12 +101,13 @@ function _ts_decorate2(decorators, target, key, desc) {
73
101
  return c > 3 && r && Object.defineProperty(target, key, r), r;
74
102
  }
75
103
  __name(_ts_decorate2, "_ts_decorate");
104
+ var require2 = createRequire(import.meta.url);
76
105
  var PluginNotFoundError = class extends Error {
77
106
  static {
78
107
  __name(this, "PluginNotFoundError");
79
108
  }
80
- constructor(pluginID) {
81
- super(`Plugin not found: ${pluginID}`);
109
+ constructor(pluginKey) {
110
+ super(`Plugin not found: ${pluginKey}`);
82
111
  this.name = "PluginNotFoundError";
83
112
  }
84
113
  };
@@ -86,8 +115,8 @@ var PluginLoadError = class extends Error {
86
115
  static {
87
116
  __name(this, "PluginLoadError");
88
117
  }
89
- constructor(pluginID, reason) {
90
- super(`Failed to load plugin ${pluginID}: ${reason}`);
118
+ constructor(pluginKey, reason) {
119
+ super(`Failed to load plugin ${pluginKey}: ${reason}`);
91
120
  this.name = "PluginLoadError";
92
121
  }
93
122
  };
@@ -97,46 +126,100 @@ var PluginLoaderService = class _PluginLoaderService {
97
126
  }
98
127
  logger = new Logger(_PluginLoaderService.name);
99
128
  pluginInstances = /* @__PURE__ */ new Map();
100
- async loadPlugin(pluginID) {
101
- const cached = this.pluginInstances.get(pluginID);
129
+ /** 记录每个插件的加载版本(时间戳),用于 ESM 缓存绕过 */
130
+ pluginVersions = /* @__PURE__ */ new Map();
131
+ async loadPlugin(pluginKey) {
132
+ const cached = this.pluginInstances.get(pluginKey);
102
133
  if (cached) {
103
- this.logger.debug(`Using cached plugin instance: ${pluginID}`);
134
+ this.logger.debug(`Using cached plugin instance: ${pluginKey}`);
104
135
  return cached;
105
136
  }
106
- this.logger.log(`Loading plugin: ${pluginID}`);
137
+ this.logger.log(`Loading plugin: ${pluginKey}`);
107
138
  try {
108
- const pluginPackage = (await import(pluginID)).default;
139
+ const resolvedPath = require2.resolve(pluginKey);
140
+ const version = this.pluginVersions.get(pluginKey) ?? 0;
141
+ const fileUrl = pathToFileURL(resolvedPath).href;
142
+ const importPath = version > 0 ? `${fileUrl}?v=${version}` : fileUrl;
143
+ const mod = await import(importPath);
144
+ const pluginPackage = mod.default ?? mod;
109
145
  if (typeof pluginPackage.create !== "function") {
110
- throw new PluginLoadError(pluginID, "Plugin does not export create() function");
146
+ throw new PluginLoadError(pluginKey, "Plugin does not export create() function");
111
147
  }
112
148
  const instance = pluginPackage.create();
113
- this.pluginInstances.set(pluginID, instance);
114
- this.logger.log(`Plugin loaded successfully: ${pluginID}`);
149
+ this.pluginInstances.set(pluginKey, instance);
150
+ this.logger.log(`Plugin loaded successfully: ${pluginKey}`);
115
151
  return instance;
116
152
  } catch (error) {
117
153
  if (error.code === "MODULE_NOT_FOUND") {
118
- throw new PluginNotFoundError(pluginID);
154
+ throw new PluginNotFoundError(pluginKey);
119
155
  }
120
- throw new PluginLoadError(pluginID, error instanceof Error ? error.message : String(error));
156
+ throw new PluginLoadError(pluginKey, error instanceof Error ? error.message : String(error));
121
157
  }
122
158
  }
123
- isPluginInstalled(pluginID) {
159
+ isPluginInstalled(pluginKey) {
124
160
  try {
125
- __require.resolve(pluginID);
161
+ require2.resolve(pluginKey);
126
162
  return true;
127
163
  } catch {
128
164
  return false;
129
165
  }
130
166
  }
131
- clearCache(pluginID) {
132
- if (pluginID) {
133
- this.pluginInstances.delete(pluginID);
134
- this.logger.log(`Cleared cache for plugin: ${pluginID}`);
167
+ /**
168
+ * 清除插件缓存
169
+ * - 清除应用层 pluginInstances 缓存
170
+ * - 清除 Node.js CJS 模块缓存(require.cache)
171
+ * - 更新版本号,下次 import 时绕过 ESM 缓存
172
+ * @param pluginKey - 插件标识,不传则清除所有
173
+ */
174
+ clearCache(pluginKey) {
175
+ if (pluginKey) {
176
+ this.pluginInstances.delete(pluginKey);
177
+ this.clearNodeModuleCache(pluginKey);
178
+ this.pluginVersions.set(pluginKey, Date.now());
179
+ this.logger.log(`Cleared cache for plugin: ${pluginKey}`);
135
180
  } else {
181
+ for (const key of this.pluginInstances.keys()) {
182
+ this.clearNodeModuleCache(key);
183
+ this.pluginVersions.set(key, Date.now());
184
+ }
136
185
  this.pluginInstances.clear();
137
186
  this.logger.log("Cleared all plugin caches");
138
187
  }
139
188
  }
189
+ /**
190
+ * 清除 CJS 模块缓存
191
+ */
192
+ clearNodeModuleCache(pluginKey) {
193
+ try {
194
+ const resolvedPath = require2.resolve(pluginKey);
195
+ const mod = require2.cache[resolvedPath];
196
+ if (mod) {
197
+ this.clearModuleAndChildren(mod, pluginKey);
198
+ }
199
+ delete require2.cache[resolvedPath];
200
+ } catch {
201
+ }
202
+ }
203
+ /**
204
+ * 递归清除子模块缓存
205
+ */
206
+ clearModuleAndChildren(mod, pluginKey) {
207
+ const pluginScope = pluginKey.startsWith("@") ? pluginKey.split("/").slice(0, 2).join("/") : pluginKey;
208
+ mod.children.forEach((child) => {
209
+ if (child.filename.includes(`node_modules/${pluginScope}`)) {
210
+ this.clearModuleAndChildren(child, pluginKey);
211
+ delete require2.cache[child.filename];
212
+ }
213
+ });
214
+ }
215
+ /**
216
+ * 强制重新加载插件
217
+ * @param pluginKey - 插件标识
218
+ */
219
+ async reloadPlugin(pluginKey) {
220
+ this.clearCache(pluginKey);
221
+ return this.loadPlugin(pluginKey);
222
+ }
140
223
  };
141
224
  PluginLoaderService = _ts_decorate2([
142
225
  Injectable2()
@@ -147,6 +230,29 @@ import { Injectable as Injectable3, Logger as Logger2, Inject } from "@nestjs/co
147
230
  import { RequestContextService, PLATFORM_HTTP_CLIENT } from "@lark-apaas/nestjs-common";
148
231
  import * as fs from "fs";
149
232
  import * as path from "path";
233
+ import * as chokidar from "chokidar";
234
+
235
+ // src/utils/log-utils.ts
236
+ var DEFAULT_MAX_LENGTH = 1e3;
237
+ function truncateString(str, maxLength) {
238
+ if (str.length <= maxLength) {
239
+ return str;
240
+ }
241
+ return str.slice(0, maxLength) + `... [truncated, total ${str.length} chars]`;
242
+ }
243
+ __name(truncateString, "truncateString");
244
+ function stringifyForLog(value, maxLength = DEFAULT_MAX_LENGTH) {
245
+ try {
246
+ const str = JSON.stringify(value, null, 2);
247
+ return truncateString(str, maxLength);
248
+ } catch {
249
+ const str = String(value);
250
+ return truncateString(str, maxLength);
251
+ }
252
+ }
253
+ __name(stringifyForLog, "stringifyForLog");
254
+
255
+ // src/services/capability.service.ts
150
256
  function _ts_decorate3(decorators, target, key, desc) {
151
257
  var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
152
258
  if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
@@ -177,8 +283,8 @@ var ActionNotFoundError = class extends Error {
177
283
  static {
178
284
  __name(this, "ActionNotFoundError");
179
285
  }
180
- constructor(pluginID, actionName) {
181
- super(`Action '${actionName}' not found in plugin ${pluginID}`);
286
+ constructor(pluginKey, actionName) {
287
+ super(`Action '${actionName}' not found in plugin ${pluginKey}`);
182
288
  this.name = "ActionNotFoundError";
183
289
  }
184
290
  };
@@ -187,25 +293,150 @@ var CapabilityService = class _CapabilityService {
187
293
  __name(this, "CapabilityService");
188
294
  }
189
295
  requestContextService;
190
- httpClient;
296
+ platformHttpClient;
191
297
  pluginLoaderService;
192
298
  templateEngineService;
193
299
  logger = new Logger2(_CapabilityService.name);
194
300
  capabilities = /* @__PURE__ */ new Map();
301
+ /** 文件路径到 capability id 的映射,用于文件删除时查找 */
302
+ filePathToId = /* @__PURE__ */ new Map();
195
303
  capabilitiesDir;
196
- constructor(requestContextService, httpClient, pluginLoaderService, templateEngineService) {
304
+ fileWatcher = null;
305
+ options = {};
306
+ constructor(requestContextService, platformHttpClient, pluginLoaderService, templateEngineService) {
197
307
  this.requestContextService = requestContextService;
198
- this.httpClient = httpClient;
308
+ this.platformHttpClient = platformHttpClient;
199
309
  this.pluginLoaderService = pluginLoaderService;
200
310
  this.templateEngineService = templateEngineService;
201
- this.capabilitiesDir = path.join(process.cwd(), "server/capabilities");
311
+ const isDev = process.env.NODE_ENV === "development";
312
+ this.capabilitiesDir = path.join(process.cwd(), isDev ? "server/capabilities" : "capabilities");
313
+ }
314
+ /**
315
+ * 设置模块配置
316
+ */
317
+ setOptions(options) {
318
+ this.options = options;
319
+ if (options.capabilitiesDir) {
320
+ this.capabilitiesDir = options.capabilitiesDir;
321
+ }
202
322
  }
203
323
  setCapabilitiesDir(dir) {
204
324
  this.capabilitiesDir = dir;
205
325
  }
206
326
  async onModuleInit() {
207
327
  await this.loadCapabilities();
328
+ if (this.options.enableWatching) {
329
+ this.startWatching();
330
+ }
331
+ }
332
+ async onModuleDestroy() {
333
+ this.stopWatching();
334
+ }
335
+ // ==================== 文件监听相关方法 ====================
336
+ /**
337
+ * 启动文件监听(沙箱环境自动调用)
338
+ */
339
+ startWatching() {
340
+ if (this.fileWatcher) {
341
+ return;
342
+ }
343
+ if (!fs.existsSync(this.capabilitiesDir)) {
344
+ this.logger.warn(`Cannot start watching: directory not found: ${this.capabilitiesDir}`);
345
+ return;
346
+ }
347
+ const pattern = path.join(this.capabilitiesDir, "*.json");
348
+ const debounce = this.options.watchDebounce ?? 300;
349
+ this.logger.log(`Starting file watcher: ${pattern}`);
350
+ this.fileWatcher = chokidar.watch(pattern, {
351
+ persistent: true,
352
+ ignoreInitial: true,
353
+ awaitWriteFinish: {
354
+ // 等待写入完成
355
+ stabilityThreshold: debounce,
356
+ pollInterval: 100
357
+ }
358
+ });
359
+ this.fileWatcher.on("add", (filePath) => this.handleFileAdd(filePath)).on("change", (filePath) => this.handleFileChange(filePath)).on("unlink", (filePath) => this.handleFileUnlink(filePath)).on("error", (error) => this.logger.error("File watcher error:", error));
360
+ this.logger.log("File watcher started");
361
+ }
362
+ /**
363
+ * 停止文件监听
364
+ */
365
+ stopWatching() {
366
+ if (this.fileWatcher) {
367
+ this.fileWatcher.close();
368
+ this.fileWatcher = null;
369
+ this.logger.log("File watcher stopped");
370
+ }
371
+ }
372
+ /**
373
+ * 重新加载所有能力配置
374
+ */
375
+ async reloadAllCapabilities() {
376
+ this.capabilities.clear();
377
+ this.filePathToId.clear();
378
+ await this.loadCapabilities();
379
+ }
380
+ handleFileAdd(filePath) {
381
+ this.logger.log(`Capability file added: ${filePath}`);
382
+ try {
383
+ this.loadCapabilityFromFile(filePath);
384
+ } catch (error) {
385
+ this.logger.error(`Failed to load new capability: ${filePath}`, error);
386
+ }
387
+ }
388
+ handleFileChange(filePath) {
389
+ this.logger.log(`Capability file changed: ${filePath}`);
390
+ try {
391
+ this.reloadCapabilityFromFile(filePath);
392
+ } catch (error) {
393
+ this.logger.error(`Failed to reload capability: ${filePath}`, error);
394
+ }
395
+ }
396
+ handleFileUnlink(filePath) {
397
+ this.logger.log(`Capability file removed: ${filePath}`);
398
+ this.removeCapabilityByFile(filePath);
399
+ }
400
+ /**
401
+ * 从文件加载单个能力配置
402
+ */
403
+ loadCapabilityFromFile(filePath) {
404
+ const content = fs.readFileSync(filePath, "utf-8");
405
+ const config = JSON.parse(content);
406
+ if (!config.id) {
407
+ this.logger.warn(`Skipping capability without id: ${filePath}`);
408
+ return;
409
+ }
410
+ this.capabilities.set(config.id, config);
411
+ this.filePathToId.set(filePath, config.id);
412
+ this.logger.log(`Loaded capability: ${config.id} (${config.name})`);
208
413
  }
414
+ /**
415
+ * 重新加载单个能力配置
416
+ */
417
+ reloadCapabilityFromFile(filePath) {
418
+ const oldId = this.filePathToId.get(filePath);
419
+ if (oldId) {
420
+ const oldConfig = this.capabilities.get(oldId);
421
+ if (oldConfig) {
422
+ this.pluginLoaderService.clearCache(oldConfig.pluginKey);
423
+ }
424
+ this.capabilities.delete(oldId);
425
+ }
426
+ this.loadCapabilityFromFile(filePath);
427
+ }
428
+ /**
429
+ * 根据文件路径移除能力配置
430
+ */
431
+ removeCapabilityByFile(filePath) {
432
+ const capabilityId = this.filePathToId.get(filePath);
433
+ if (capabilityId) {
434
+ this.capabilities.delete(capabilityId);
435
+ this.filePathToId.delete(filePath);
436
+ this.logger.log(`Removed capability: ${capabilityId}`);
437
+ }
438
+ }
439
+ // ==================== 原有方法 ====================
209
440
  async loadCapabilities() {
210
441
  this.logger.log(`Loading capabilities from ${this.capabilitiesDir}`);
211
442
  if (!fs.existsSync(this.capabilitiesDir)) {
@@ -216,14 +447,7 @@ var CapabilityService = class _CapabilityService {
216
447
  for (const file of files) {
217
448
  try {
218
449
  const filePath = path.join(this.capabilitiesDir, file);
219
- const content = fs.readFileSync(filePath, "utf-8");
220
- const config = JSON.parse(content);
221
- if (!config.id) {
222
- this.logger.warn(`Skipping capability without id: ${file}`);
223
- continue;
224
- }
225
- this.capabilities.set(config.id, config);
226
- this.logger.log(`Loaded capability: ${config.id} (${config.name})`);
450
+ this.loadCapabilityFromFile(filePath);
227
451
  } catch (error) {
228
452
  this.logger.error(`Failed to load capability from ${file}:`, error);
229
453
  }
@@ -241,56 +465,246 @@ var CapabilityService = class _CapabilityService {
241
465
  if (!config) {
242
466
  throw new CapabilityNotFoundError(capabilityId);
243
467
  }
468
+ return this.createExecutor(config);
469
+ }
470
+ /**
471
+ * 使用传入的配置加载能力执行器
472
+ * 用于 debug 场景,支持用户传入自定义配置
473
+ */
474
+ loadWithConfig(config) {
475
+ return this.createExecutor(config);
476
+ }
477
+ createExecutor(config) {
244
478
  return {
245
479
  call: /* @__PURE__ */ __name(async (actionName, input, contextOverride) => {
246
- return this.execute(config, actionName, input, contextOverride);
247
- }, "call")
480
+ return this.executeCall(config, actionName, input, contextOverride);
481
+ }, "call"),
482
+ callStream: /* @__PURE__ */ __name((actionName, input, contextOverride) => {
483
+ return this.executeCallStream(config, actionName, input, contextOverride);
484
+ }, "callStream"),
485
+ callStreamWithEvents: /* @__PURE__ */ __name((actionName, input, contextOverride) => {
486
+ return this.executeCallStreamWithEvents(config, actionName, input, contextOverride);
487
+ }, "callStreamWithEvents"),
488
+ isStream: /* @__PURE__ */ __name(async (actionName) => {
489
+ return this.checkIsStream(config, actionName);
490
+ }, "isStream")
248
491
  };
249
492
  }
250
- async execute(config, actionName, input, contextOverride) {
493
+ /**
494
+ * 检查 action 是否为流式
495
+ */
496
+ async checkIsStream(config, actionName) {
497
+ const pluginInstance = await this.pluginLoaderService.loadPlugin(config.pluginKey);
498
+ if (!pluginInstance.hasAction(actionName)) {
499
+ throw new ActionNotFoundError(config.pluginKey, actionName);
500
+ }
501
+ return pluginInstance.isStreamAction?.(actionName) ?? false;
502
+ }
503
+ /**
504
+ * 执行 capability(始终返回 Promise)
505
+ * - unary action: 直接返回结果
506
+ * - stream action: 内部聚合所有 chunk 后返回
507
+ */
508
+ async executeCall(config, actionName, input, contextOverride) {
251
509
  const startTime = Date.now();
510
+ const loggerContext = {
511
+ capability_id: config.id,
512
+ plugin_key: config.pluginKey,
513
+ action: actionName
514
+ };
252
515
  try {
253
- const pluginInstance = await this.pluginLoaderService.loadPlugin(config.pluginID);
516
+ const pluginInstance = await this.pluginLoaderService.loadPlugin(config.pluginKey);
254
517
  if (!pluginInstance.hasAction(actionName)) {
255
- throw new ActionNotFoundError(config.pluginID, actionName);
518
+ throw new ActionNotFoundError(config.pluginKey, actionName);
256
519
  }
257
- const resolvedParams = this.templateEngineService.resolve(config.formValue, input);
520
+ const resolvedParams = config.formValue ? this.templateEngineService.resolve(config.formValue, input) : input;
258
521
  const context = this.buildActionContext(contextOverride);
259
- this.logger.log({
260
- message: "Executing capability",
261
- capabilityId: config.id,
262
- action: actionName,
263
- pluginID: config.pluginID
522
+ const isStream = pluginInstance.isStreamAction?.(actionName) ?? false;
523
+ this.logger.log("Executing capability (call)", {
524
+ ...loggerContext,
525
+ is_stream: isStream,
526
+ input: stringifyForLog(input)
264
527
  });
265
- const result = await pluginInstance.run(actionName, context, resolvedParams);
266
- this.logger.log({
267
- message: "Capability executed successfully",
268
- capabilityId: config.id,
269
- action: actionName,
270
- duration: Date.now() - startTime
528
+ let result;
529
+ if (isStream && pluginInstance.runStream) {
530
+ const chunks = [];
531
+ for await (const chunk of pluginInstance.runStream(actionName, context, resolvedParams)) {
532
+ chunks.push(chunk);
533
+ }
534
+ result = pluginInstance.aggregate ? pluginInstance.aggregate(actionName, chunks) : chunks;
535
+ } else {
536
+ result = await pluginInstance.run(actionName, context, resolvedParams);
537
+ }
538
+ this.logger.log("Capability (call) executed successfully", {
539
+ ...loggerContext,
540
+ duration_ms: Date.now() - startTime,
541
+ output: stringifyForLog(result)
271
542
  });
272
543
  return result;
273
544
  } catch (error) {
274
- this.logger.error({
275
- message: "Capability execution failed",
276
- capabilityId: config.id,
277
- action: actionName,
278
- error: error instanceof Error ? error.message : String(error),
279
- duration: Date.now() - startTime
545
+ this.logger.error("Capability (call) execution failed", {
546
+ ...loggerContext,
547
+ duration_ms: Date.now() - startTime,
548
+ error: error instanceof Error ? error.message : String(error)
549
+ });
550
+ throw error;
551
+ }
552
+ }
553
+ /**
554
+ * 流式执行 capability
555
+ * - stream action: 返回原始 AsyncIterable
556
+ * - unary action: 包装为单次 yield
557
+ */
558
+ async *executeCallStream(config, actionName, input, contextOverride) {
559
+ const startTime = Date.now();
560
+ const loggerContext = {
561
+ capability_id: config.id,
562
+ plugin_key: config.pluginKey,
563
+ action: actionName
564
+ };
565
+ const chunks = [];
566
+ try {
567
+ const pluginInstance = await this.pluginLoaderService.loadPlugin(config.pluginKey);
568
+ if (!pluginInstance.hasAction(actionName)) {
569
+ throw new ActionNotFoundError(config.pluginKey, actionName);
570
+ }
571
+ const resolvedParams = config.formValue ? this.templateEngineService.resolve(config.formValue, input) : input;
572
+ const context = this.buildActionContext(contextOverride);
573
+ const isStream = pluginInstance.isStreamAction?.(actionName) ?? false;
574
+ this.logger.log("Executing capability (stream)", {
575
+ ...loggerContext,
576
+ is_stream: isStream,
577
+ input: stringifyForLog(input)
578
+ });
579
+ if (isStream && pluginInstance.runStream) {
580
+ for await (const chunk of pluginInstance.runStream(actionName, context, resolvedParams)) {
581
+ chunks.push(chunk);
582
+ yield chunk;
583
+ }
584
+ } else {
585
+ const result = await pluginInstance.run(actionName, context, resolvedParams);
586
+ chunks.push(result);
587
+ yield result;
588
+ }
589
+ const aggregatedResult = pluginInstance.aggregate ? pluginInstance.aggregate(actionName, chunks) : chunks;
590
+ this.logger.log("Capability (stream) executed successfully", {
591
+ ...loggerContext,
592
+ duration_ms: Date.now() - startTime,
593
+ output: stringifyForLog(aggregatedResult)
594
+ });
595
+ } catch (error) {
596
+ this.logger.error("Capability (stream) execution failed", {
597
+ ...loggerContext,
598
+ duration_ms: Date.now() - startTime,
599
+ error: error instanceof Error ? error.message : String(error)
280
600
  });
281
601
  throw error;
282
602
  }
283
603
  }
604
+ /**
605
+ * 流式执行 capability,返回带事件协议的流
606
+ * - 优先使用 pluginInstance.runStreamWithEvents
607
+ * - 如果插件不支持,则包装 runStream/run 为 StreamEvent
608
+ */
609
+ async *executeCallStreamWithEvents(config, actionName, input, contextOverride) {
610
+ const startTime = Date.now();
611
+ const loggerContext = {
612
+ capability_id: config.id,
613
+ plugin_key: config.pluginKey,
614
+ action: actionName
615
+ };
616
+ const chunks = [];
617
+ try {
618
+ const pluginInstance = await this.pluginLoaderService.loadPlugin(config.pluginKey);
619
+ if (!pluginInstance.hasAction(actionName)) {
620
+ throw new ActionNotFoundError(config.pluginKey, actionName);
621
+ }
622
+ const resolvedParams = config.formValue ? this.templateEngineService.resolve(config.formValue, input) : input;
623
+ const context = this.buildActionContext(contextOverride);
624
+ const isStream = pluginInstance.isStreamAction?.(actionName) ?? false;
625
+ this.logger.log("Executing capability (streamWithEvents)", {
626
+ ...loggerContext,
627
+ is_stream: isStream,
628
+ input: stringifyForLog(input)
629
+ });
630
+ if (pluginInstance.runStreamWithEvents) {
631
+ for await (const event of pluginInstance.runStreamWithEvents(actionName, context, resolvedParams)) {
632
+ if (event.type === "data") {
633
+ chunks.push(event.data);
634
+ yield event;
635
+ } else if (event.type === "done") {
636
+ const aggregatedResult2 = pluginInstance.aggregate ? pluginInstance.aggregate(actionName, chunks) : chunks;
637
+ yield {
638
+ type: "done",
639
+ metadata: {
640
+ ...event.metadata,
641
+ aggregated: aggregatedResult2
642
+ }
643
+ };
644
+ } else {
645
+ yield event;
646
+ }
647
+ }
648
+ } else {
649
+ if (isStream && pluginInstance.runStream) {
650
+ for await (const chunk of pluginInstance.runStream(actionName, context, resolvedParams)) {
651
+ chunks.push(chunk);
652
+ yield {
653
+ type: "data",
654
+ data: chunk
655
+ };
656
+ }
657
+ } else {
658
+ const result = await pluginInstance.run(actionName, context, resolvedParams);
659
+ chunks.push(result);
660
+ yield {
661
+ type: "data",
662
+ data: result
663
+ };
664
+ }
665
+ const aggregatedResult2 = pluginInstance.aggregate ? pluginInstance.aggregate(actionName, chunks) : chunks;
666
+ yield {
667
+ type: "done",
668
+ metadata: {
669
+ chunks: chunks.length,
670
+ duration: Date.now() - startTime,
671
+ aggregated: aggregatedResult2
672
+ }
673
+ };
674
+ }
675
+ const aggregatedResult = pluginInstance.aggregate ? pluginInstance.aggregate(actionName, chunks) : chunks;
676
+ this.logger.log("Capability (streamWithEvents) executed successfully", {
677
+ ...loggerContext,
678
+ duration_ms: Date.now() - startTime,
679
+ output: stringifyForLog(aggregatedResult)
680
+ });
681
+ } catch (error) {
682
+ this.logger.error("Capability (streamWithEvents) execution failed", {
683
+ ...loggerContext,
684
+ duration_ms: Date.now() - startTime,
685
+ error: error instanceof Error ? error.message : String(error)
686
+ });
687
+ yield {
688
+ type: "error",
689
+ error: {
690
+ code: "EXECUTION_ERROR",
691
+ message: error instanceof Error ? error.message : String(error)
692
+ }
693
+ };
694
+ }
695
+ }
284
696
  buildActionContext(override) {
285
697
  return {
286
698
  logger: this.logger,
287
- httpClient: this.httpClient,
288
- userContext: override?.userContext ?? this.getUserContext()
699
+ platformHttpClient: this.platformHttpClient,
700
+ userContext: override?.userContext ?? this.getUserContext(),
701
+ isDebug: override?.isDebug ?? false
289
702
  };
290
703
  }
291
704
  getUserContext() {
292
705
  const ctx = this.requestContextService.getContext();
293
706
  return {
707
+ appId: ctx?.appId ?? "",
294
708
  userId: ctx?.userId ?? "",
295
709
  tenantId: ctx?.tenantId ?? ""
296
710
  };
@@ -309,7 +723,7 @@ CapabilityService = _ts_decorate3([
309
723
  ], CapabilityService);
310
724
 
311
725
  // src/controllers/debug.controller.ts
312
- import { Controller, Post, Get, Param, Body, HttpException, HttpStatus } from "@nestjs/common";
726
+ import { Controller, Post, Get, Param, Body, Res, HttpException, HttpStatus, Logger as Logger3 } from "@nestjs/common";
313
727
  function _ts_decorate4(decorators, target, key, desc) {
314
728
  var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
315
729
  if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
@@ -327,31 +741,41 @@ function _ts_param2(paramIndex, decorator) {
327
741
  };
328
742
  }
329
743
  __name(_ts_param2, "_ts_param");
330
- var DebugController = class {
744
+ var DebugController = class _DebugController {
331
745
  static {
332
746
  __name(this, "DebugController");
333
747
  }
334
748
  capabilityService;
749
+ pluginLoaderService;
335
750
  templateEngineService;
336
- constructor(capabilityService, templateEngineService) {
751
+ logger = new Logger3(_DebugController.name);
752
+ constructor(capabilityService, pluginLoaderService, templateEngineService) {
337
753
  this.capabilityService = capabilityService;
754
+ this.pluginLoaderService = pluginLoaderService;
338
755
  this.templateEngineService = templateEngineService;
339
756
  }
340
757
  list() {
341
758
  const capabilities = this.capabilityService.listCapabilities();
342
759
  return {
343
- code: 0,
344
- message: "success",
345
- data: capabilities.map((c) => ({
346
- id: c.id,
347
- name: c.name,
348
- pluginID: c.pluginID,
349
- pluginVersion: c.pluginVersion
350
- }))
760
+ status_code: ErrorCodes.SUCCESS,
761
+ data: {
762
+ capabilities: capabilities.map((c) => ({
763
+ id: c.id,
764
+ name: c.name,
765
+ pluginID: c.pluginKey,
766
+ pluginVersion: c.pluginVersion
767
+ }))
768
+ }
351
769
  };
352
770
  }
353
- async debug(capabilityId, body) {
354
- const startTime = Date.now();
771
+ /**
772
+ * 获取 capability 配置
773
+ * 优先使用 body.capability,否则从服务获取
774
+ */
775
+ getCapabilityConfig(capabilityId, bodyCapability) {
776
+ if (bodyCapability) {
777
+ return bodyCapability;
778
+ }
355
779
  const config = this.capabilityService.getCapability(capabilityId);
356
780
  if (!config) {
357
781
  throw new HttpException({
@@ -360,72 +784,194 @@ var DebugController = class {
360
784
  error: "CAPABILITY_NOT_FOUND"
361
785
  }, HttpStatus.NOT_FOUND);
362
786
  }
363
- const resolvedParams = this.templateEngineService.resolve(config.formValue, body.params);
787
+ return config;
788
+ }
789
+ /**
790
+ * 获取 action 名称
791
+ * 优先使用传入的 action,否则使用插件第一个 action
792
+ */
793
+ async getActionName(pluginKey, bodyAction) {
794
+ if (bodyAction) {
795
+ return bodyAction;
796
+ }
797
+ const pluginInstance = await this.pluginLoaderService.loadPlugin(pluginKey);
798
+ const actions = pluginInstance.listActions();
799
+ if (actions.length === 0) {
800
+ throw new HttpException({
801
+ code: 1,
802
+ message: `Plugin ${pluginKey} has no actions`,
803
+ error: "NO_ACTIONS"
804
+ }, HttpStatus.BAD_REQUEST);
805
+ }
806
+ return actions[0];
807
+ }
808
+ async debug(capabilityId, body) {
809
+ const startTime = Date.now();
810
+ const params = body.params ?? {};
811
+ const config = this.getCapabilityConfig(capabilityId, body.capability);
812
+ const action = await this.getActionName(config.pluginKey, body.action);
813
+ const resolvedParams = this.templateEngineService.resolve(config.formValue, params);
364
814
  try {
365
- const result = await this.capabilityService.load(capabilityId).call(body.action, body.params);
815
+ const result = await this.capabilityService.loadWithConfig(config).call(action, params, {
816
+ isDebug: true
817
+ });
366
818
  return {
367
- code: 0,
368
- message: "success",
369
- data: result,
370
- debug: {
371
- capabilityConfig: config,
372
- resolvedParams,
373
- duration: Date.now() - startTime,
374
- pluginID: config.pluginID
819
+ status_code: ErrorCodes.SUCCESS,
820
+ data: {
821
+ output: result,
822
+ debug: {
823
+ capabilityConfig: config,
824
+ resolvedParams,
825
+ duration: Date.now() - startTime,
826
+ pluginID: config.pluginKey,
827
+ action
828
+ }
375
829
  }
376
830
  };
377
831
  } catch (error) {
378
- const duration = Date.now() - startTime;
379
832
  if (error instanceof CapabilityNotFoundError) {
380
833
  throw new HttpException({
381
- code: 1,
382
- message: error.message,
383
- error: "CAPABILITY_NOT_FOUND",
384
- debug: {
385
- duration
386
- }
834
+ status_code: ErrorCodes.CAPABILITY_NOT_FOUND,
835
+ error_msg: `Capability not found: ${capabilityId}`
387
836
  }, HttpStatus.NOT_FOUND);
388
837
  }
389
838
  if (error instanceof PluginNotFoundError) {
390
839
  throw new HttpException({
391
- code: 1,
392
- message: error.message,
393
- error: "PLUGIN_NOT_FOUND",
394
- debug: {
395
- duration,
396
- pluginID: config.pluginID
397
- }
840
+ status_code: ErrorCodes.PLUGIN_NOT_FOUND,
841
+ error_msg: `Plugin not found: ${config.pluginKey}`
398
842
  }, HttpStatus.INTERNAL_SERVER_ERROR);
399
843
  }
400
844
  if (error instanceof ActionNotFoundError) {
401
845
  throw new HttpException({
402
- code: 1,
403
- message: error.message,
404
- error: "ACTION_NOT_FOUND",
405
- debug: {
406
- duration,
407
- pluginID: config.pluginID
408
- }
846
+ status_code: ErrorCodes.ACTION_NOT_FOUND,
847
+ error_msg: `Action '${action}' not found in plugin ${config.pluginKey}`
409
848
  }, HttpStatus.BAD_REQUEST);
410
849
  }
411
850
  throw new HttpException({
412
- code: 1,
413
- message: error instanceof Error ? error.message : String(error),
414
- error: "EXECUTION_ERROR",
415
- debug: {
416
- duration,
417
- pluginID: config.pluginID,
418
- resolvedParams
419
- }
851
+ status_code: ErrorCodes.EXECUTION_ERROR,
852
+ error_msg: `Execution failed: ${error instanceof Error ? error.message : String(error)}`
420
853
  }, HttpStatus.INTERNAL_SERVER_ERROR);
421
854
  }
422
855
  }
856
+ async debugStream(capabilityId, body, res) {
857
+ const params = body.params ?? {};
858
+ res.setHeader("Content-Type", "text/event-stream");
859
+ res.setHeader("Cache-Control", "no-cache");
860
+ res.setHeader("Connection", "keep-alive");
861
+ try {
862
+ const config = this.getCapabilityConfig(capabilityId, body.capability);
863
+ const action = await this.getActionName(config.pluginKey, body.action);
864
+ const loggerContext = {
865
+ capability_id: config.id,
866
+ plugin_key: config.pluginKey,
867
+ action
868
+ };
869
+ this.logger.log(`Executing capability (stream)`, {
870
+ ...loggerContext,
871
+ input: stringifyForLog(params)
872
+ });
873
+ const capability = this.capabilityService.loadWithConfig(config);
874
+ const eventStream = capability.callStreamWithEvents(action, params, {
875
+ isDebug: true
876
+ });
877
+ let pendingChunk = null;
878
+ for await (const event of eventStream) {
879
+ switch (event.type) {
880
+ case "data": {
881
+ if (pendingChunk !== null) {
882
+ const response = {
883
+ status_code: ErrorCodes.SUCCESS,
884
+ data: {
885
+ type: "content",
886
+ delta: pendingChunk
887
+ }
888
+ };
889
+ res.write(`data: ${JSON.stringify(response)}
890
+
891
+ `);
892
+ }
893
+ pendingChunk = event.data;
894
+ break;
895
+ }
896
+ case "done": {
897
+ if (pendingChunk !== null) {
898
+ const response = {
899
+ status_code: ErrorCodes.SUCCESS,
900
+ data: {
901
+ type: "content",
902
+ delta: pendingChunk,
903
+ finished: true
904
+ }
905
+ };
906
+ res.write(`data: ${JSON.stringify(response)}
907
+
908
+ `);
909
+ }
910
+ this.logger.log(`Capability (stream) executed successfully`, {
911
+ ...loggerContext,
912
+ duration_ms: event.metadata.duration,
913
+ output: stringifyForLog(event.metadata.aggregated)
914
+ });
915
+ res.end();
916
+ return;
917
+ }
918
+ case "error": {
919
+ const response = {
920
+ status_code: ErrorCodes.SUCCESS,
921
+ data: {
922
+ type: "error",
923
+ error: {
924
+ code: 0,
925
+ message: event.error.message
926
+ }
927
+ }
928
+ };
929
+ res.write(`data: ${JSON.stringify(response)}
930
+
931
+ `);
932
+ res.end();
933
+ return;
934
+ }
935
+ }
936
+ }
937
+ if (pendingChunk !== null) {
938
+ const response = {
939
+ status_code: ErrorCodes.SUCCESS,
940
+ data: {
941
+ type: "content",
942
+ delta: pendingChunk,
943
+ finished: true
944
+ }
945
+ };
946
+ res.write(`data: ${JSON.stringify(response)}
947
+
948
+ `);
949
+ }
950
+ res.end();
951
+ } catch (error) {
952
+ const errorMsg = error instanceof Error ? error.message : String(error);
953
+ const response = {
954
+ status_code: ErrorCodes.SUCCESS,
955
+ data: {
956
+ type: "error",
957
+ error: {
958
+ code: 0,
959
+ message: errorMsg
960
+ }
961
+ }
962
+ };
963
+ res.write(`data: ${JSON.stringify(response)}
964
+
965
+ `);
966
+ res.end();
967
+ }
968
+ }
423
969
  };
424
970
  _ts_decorate4([
425
971
  Get("list"),
426
972
  _ts_metadata2("design:type", Function),
427
973
  _ts_metadata2("design:paramtypes", []),
428
- _ts_metadata2("design:returntype", typeof ListResponse === "undefined" ? Object : ListResponse)
974
+ _ts_metadata2("design:returntype", typeof SuccessResponse === "undefined" ? Object : SuccessResponse)
429
975
  ], DebugController.prototype, "list", null);
430
976
  _ts_decorate4([
431
977
  Post("debug/:capability_id"),
@@ -434,21 +980,35 @@ _ts_decorate4([
434
980
  _ts_metadata2("design:type", Function),
435
981
  _ts_metadata2("design:paramtypes", [
436
982
  String,
437
- typeof ExecuteRequestBody === "undefined" ? Object : ExecuteRequestBody
983
+ typeof DebugRequestBody === "undefined" ? Object : DebugRequestBody
438
984
  ]),
439
985
  _ts_metadata2("design:returntype", Promise)
440
986
  ], DebugController.prototype, "debug", null);
987
+ _ts_decorate4([
988
+ Post("debug/:capability_id/stream"),
989
+ _ts_param2(0, Param("capability_id")),
990
+ _ts_param2(1, Body()),
991
+ _ts_param2(2, Res()),
992
+ _ts_metadata2("design:type", Function),
993
+ _ts_metadata2("design:paramtypes", [
994
+ String,
995
+ typeof DebugRequestBody === "undefined" ? Object : DebugRequestBody,
996
+ typeof Response === "undefined" ? Object : Response
997
+ ]),
998
+ _ts_metadata2("design:returntype", Promise)
999
+ ], DebugController.prototype, "debugStream", null);
441
1000
  DebugController = _ts_decorate4([
442
1001
  Controller("__innerapi__/capability"),
443
1002
  _ts_metadata2("design:type", Function),
444
1003
  _ts_metadata2("design:paramtypes", [
445
1004
  typeof CapabilityService === "undefined" ? Object : CapabilityService,
1005
+ typeof PluginLoaderService === "undefined" ? Object : PluginLoaderService,
446
1006
  typeof TemplateEngineService === "undefined" ? Object : TemplateEngineService
447
1007
  ])
448
1008
  ], DebugController);
449
1009
 
450
1010
  // src/controllers/webhook.controller.ts
451
- import { Controller as Controller2, Get as Get2, Post as Post2, Param as Param2, Body as Body2, HttpException as HttpException2, HttpStatus as HttpStatus2 } from "@nestjs/common";
1011
+ import { Controller as Controller2, Get as Get2, Post as Post2, Param as Param2, Body as Body2, Res as Res2, HttpException as HttpException2, HttpStatus as HttpStatus2, Logger as Logger4 } from "@nestjs/common";
452
1012
  function _ts_decorate5(decorators, target, key, desc) {
453
1013
  var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
454
1014
  if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
@@ -466,71 +1026,176 @@ function _ts_param3(paramIndex, decorator) {
466
1026
  };
467
1027
  }
468
1028
  __name(_ts_param3, "_ts_param");
469
- var WebhookController = class {
1029
+ var WebhookController = class _WebhookController {
470
1030
  static {
471
1031
  __name(this, "WebhookController");
472
1032
  }
473
1033
  capabilityService;
1034
+ logger = new Logger4(_WebhookController.name);
474
1035
  constructor(capabilityService) {
475
1036
  this.capabilityService = capabilityService;
476
1037
  }
477
1038
  list() {
478
1039
  const capabilities = this.capabilityService.listCapabilities();
479
1040
  return {
480
- code: 0,
481
- message: "success",
482
- data: capabilities.map((c) => ({
483
- id: c.id,
484
- name: c.name,
485
- description: c.description,
486
- pluginID: c.pluginID,
487
- pluginVersion: c.pluginVersion
488
- }))
1041
+ status_code: ErrorCodes.SUCCESS,
1042
+ data: {
1043
+ capabilities: capabilities.map((c) => ({
1044
+ id: c.id,
1045
+ name: c.name,
1046
+ pluginID: c.pluginKey,
1047
+ pluginVersion: c.pluginVersion
1048
+ }))
1049
+ }
489
1050
  };
490
1051
  }
491
1052
  async execute(capabilityId, body) {
492
1053
  try {
493
1054
  const result = await this.capabilityService.load(capabilityId).call(body.action, body.params);
494
1055
  return {
495
- code: 0,
496
- message: "success",
497
- data: result
1056
+ status_code: ErrorCodes.SUCCESS,
1057
+ data: {
1058
+ output: result
1059
+ }
498
1060
  };
499
1061
  } catch (error) {
500
1062
  if (error instanceof CapabilityNotFoundError) {
501
1063
  throw new HttpException2({
502
- code: 1,
503
- message: error.message,
504
- error: "CAPABILITY_NOT_FOUND"
1064
+ status_code: ErrorCodes.CAPABILITY_NOT_FOUND,
1065
+ error_msg: `Capability not found: ${capabilityId}`
505
1066
  }, HttpStatus2.NOT_FOUND);
506
1067
  }
507
1068
  if (error instanceof PluginNotFoundError) {
508
1069
  throw new HttpException2({
509
- code: 1,
510
- message: error.message,
511
- error: "PLUGIN_NOT_FOUND"
1070
+ status_code: ErrorCodes.PLUGIN_NOT_FOUND,
1071
+ error_msg: `Plugin not found`
512
1072
  }, HttpStatus2.INTERNAL_SERVER_ERROR);
513
1073
  }
514
1074
  if (error instanceof ActionNotFoundError) {
515
1075
  throw new HttpException2({
516
- code: 1,
517
- message: error.message,
518
- error: "ACTION_NOT_FOUND"
1076
+ status_code: ErrorCodes.ACTION_NOT_FOUND,
1077
+ error_msg: `Action '${body.action}' not found`
519
1078
  }, HttpStatus2.BAD_REQUEST);
520
1079
  }
521
1080
  throw new HttpException2({
522
- code: 1,
523
- message: error instanceof Error ? error.message : String(error),
524
- error: "EXECUTION_ERROR"
1081
+ status_code: ErrorCodes.EXECUTION_ERROR,
1082
+ error_msg: `Execution failed: ${error instanceof Error ? error.message : String(error)}`
525
1083
  }, HttpStatus2.INTERNAL_SERVER_ERROR);
526
1084
  }
527
1085
  }
1086
+ async executeStream(capabilityId, body, res) {
1087
+ const loggerContext = {
1088
+ capability_id: capabilityId,
1089
+ action: body.action
1090
+ };
1091
+ res.setHeader("Content-Type", "text/event-stream");
1092
+ res.setHeader("Cache-Control", "no-cache");
1093
+ res.setHeader("Connection", "keep-alive");
1094
+ try {
1095
+ this.logger.log(`Executing capability (stream)`, {
1096
+ ...loggerContext,
1097
+ input: stringifyForLog(body.params)
1098
+ });
1099
+ const capability = this.capabilityService.load(capabilityId);
1100
+ const eventStream = capability.callStreamWithEvents(body.action, body.params);
1101
+ let pendingChunk = null;
1102
+ for await (const event of eventStream) {
1103
+ switch (event.type) {
1104
+ case "data": {
1105
+ if (pendingChunk !== null) {
1106
+ const response = {
1107
+ status_code: ErrorCodes.SUCCESS,
1108
+ data: {
1109
+ type: "content",
1110
+ delta: pendingChunk
1111
+ }
1112
+ };
1113
+ res.write(`data: ${JSON.stringify(response)}
1114
+
1115
+ `);
1116
+ }
1117
+ pendingChunk = event.data;
1118
+ break;
1119
+ }
1120
+ case "done": {
1121
+ if (pendingChunk !== null) {
1122
+ const response = {
1123
+ status_code: ErrorCodes.SUCCESS,
1124
+ data: {
1125
+ type: "content",
1126
+ delta: pendingChunk,
1127
+ finished: true
1128
+ }
1129
+ };
1130
+ res.write(`data: ${JSON.stringify(response)}
1131
+
1132
+ `);
1133
+ }
1134
+ this.logger.log(`Capability (stream) executed successfully`, {
1135
+ ...loggerContext,
1136
+ duration_ms: event.metadata.duration,
1137
+ output: stringifyForLog(event.metadata.aggregated)
1138
+ });
1139
+ res.end();
1140
+ return;
1141
+ }
1142
+ case "error": {
1143
+ const response = {
1144
+ status_code: ErrorCodes.SUCCESS,
1145
+ data: {
1146
+ type: "error",
1147
+ error: {
1148
+ code: 0,
1149
+ message: event.error.message
1150
+ }
1151
+ }
1152
+ };
1153
+ res.write(`data: ${JSON.stringify(response)}
1154
+
1155
+ `);
1156
+ res.end();
1157
+ return;
1158
+ }
1159
+ }
1160
+ }
1161
+ if (pendingChunk !== null) {
1162
+ const response = {
1163
+ status_code: ErrorCodes.SUCCESS,
1164
+ data: {
1165
+ type: "content",
1166
+ delta: pendingChunk,
1167
+ finished: true
1168
+ }
1169
+ };
1170
+ res.write(`data: ${JSON.stringify(response)}
1171
+
1172
+ `);
1173
+ }
1174
+ res.end();
1175
+ } catch (error) {
1176
+ const errorMsg = error instanceof Error ? error.message : String(error);
1177
+ const response = {
1178
+ status_code: ErrorCodes.SUCCESS,
1179
+ data: {
1180
+ type: "error",
1181
+ error: {
1182
+ code: 0,
1183
+ message: errorMsg
1184
+ }
1185
+ }
1186
+ };
1187
+ res.write(`data: ${JSON.stringify(response)}
1188
+
1189
+ `);
1190
+ res.end();
1191
+ }
1192
+ }
528
1193
  };
529
1194
  _ts_decorate5([
530
1195
  Get2("list"),
531
1196
  _ts_metadata3("design:type", Function),
532
1197
  _ts_metadata3("design:paramtypes", []),
533
- _ts_metadata3("design:returntype", typeof ListResponse === "undefined" ? Object : ListResponse)
1198
+ _ts_metadata3("design:returntype", typeof SuccessResponse === "undefined" ? Object : SuccessResponse)
534
1199
  ], WebhookController.prototype, "list", null);
535
1200
  _ts_decorate5([
536
1201
  Post2(":capability_id"),
@@ -543,6 +1208,19 @@ _ts_decorate5([
543
1208
  ]),
544
1209
  _ts_metadata3("design:returntype", Promise)
545
1210
  ], WebhookController.prototype, "execute", null);
1211
+ _ts_decorate5([
1212
+ Post2(":capability_id/stream"),
1213
+ _ts_param3(0, Param2("capability_id")),
1214
+ _ts_param3(1, Body2()),
1215
+ _ts_param3(2, Res2()),
1216
+ _ts_metadata3("design:type", Function),
1217
+ _ts_metadata3("design:paramtypes", [
1218
+ String,
1219
+ typeof ExecuteRequestBody === "undefined" ? Object : ExecuteRequestBody,
1220
+ typeof Response === "undefined" ? Object : Response
1221
+ ]),
1222
+ _ts_metadata3("design:returntype", Promise)
1223
+ ], WebhookController.prototype, "executeStream", null);
546
1224
  WebhookController = _ts_decorate5([
547
1225
  Controller2("api/capability"),
548
1226
  _ts_metadata3("design:type", Function),
@@ -553,7 +1231,7 @@ WebhookController = _ts_decorate5([
553
1231
 
554
1232
  // src/capability.module.ts
555
1233
  import { Module } from "@nestjs/common";
556
- import { RequestContextService as RequestContextService2, PLATFORM_HTTP_CLIENT as PLATFORM_HTTP_CLIENT2 } from "@lark-apaas/nestjs-common";
1234
+ import { CommonModule, RequestContextService as RequestContextService2, PLATFORM_HTTP_CLIENT as PLATFORM_HTTP_CLIENT2 } from "@lark-apaas/nestjs-common";
557
1235
  function _ts_decorate6(decorators, target, key, desc) {
558
1236
  var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
559
1237
  if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
@@ -580,6 +1258,9 @@ var CapabilityModule = class _CapabilityModule {
580
1258
  static forRoot(options) {
581
1259
  return {
582
1260
  module: _CapabilityModule,
1261
+ imports: [
1262
+ CommonModule
1263
+ ],
583
1264
  controllers: getControllers(),
584
1265
  providers: [
585
1266
  {
@@ -590,8 +1271,8 @@ var CapabilityModule = class _CapabilityModule {
590
1271
  provide: CapabilityService,
591
1272
  useFactory: /* @__PURE__ */ __name((requestContextService, httpClient, pluginLoader, templateEngine, moduleOptions) => {
592
1273
  const service = new CapabilityService(requestContextService, httpClient, pluginLoader, templateEngine);
593
- if (moduleOptions?.capabilitiesDir) {
594
- service.setCapabilitiesDir(moduleOptions.capabilitiesDir);
1274
+ if (moduleOptions) {
1275
+ service.setOptions(moduleOptions);
595
1276
  }
596
1277
  return service;
597
1278
  }, "useFactory"),
@@ -614,6 +1295,9 @@ var CapabilityModule = class _CapabilityModule {
614
1295
  };
615
1296
  CapabilityModule = _ts_decorate6([
616
1297
  Module({
1298
+ imports: [
1299
+ CommonModule
1300
+ ],
617
1301
  controllers: getControllers(),
618
1302
  providers: [
619
1303
  CapabilityService,
@@ -631,6 +1315,7 @@ export {
631
1315
  CapabilityNotFoundError,
632
1316
  CapabilityService,
633
1317
  DebugController,
1318
+ ErrorCodes,
634
1319
  PluginLoadError,
635
1320
  PluginLoaderService,
636
1321
  PluginNotFoundError,