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