@lark-apaas/nestjs-capability 0.0.1-alpha.6 → 0.0.1-alpha.8

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -93,14 +93,23 @@ const result = await this.capabilityService
93
93
  ### 上下文覆盖
94
94
 
95
95
  ```typescript
96
+ // 覆盖 userContext
96
97
  const result = await this.capabilityService
97
98
  .load('notify_task_created')
98
99
  .call('run', inputParams, {
99
100
  userContext: {
100
101
  userId: 'custom-user-id',
101
102
  tenantId: 'custom-tenant-id',
103
+ appId: 'custom-app-id',
102
104
  },
103
105
  });
106
+
107
+ // 设置调试模式(插件可据此返回更详细的错误信息)
108
+ const debugResult = await this.capabilityService
109
+ .load('ai_chat')
110
+ .call('chat', inputParams, {
111
+ isDebug: true,
112
+ });
104
113
  ```
105
114
 
106
115
  ## 能力配置
@@ -377,10 +386,17 @@ interface PluginActionContext {
377
386
  userContext: {
378
387
  userId: string; // 用户 ID
379
388
  tenantId: string; // 租户 ID
389
+ appId: string; // 应用 ID
380
390
  };
391
+ isDebug: boolean; // 是否为调试模式
381
392
  }
382
393
  ```
383
394
 
395
+ **isDebug 说明:**
396
+ - `DebugController` 调用时 `isDebug = true`
397
+ - `WebhookController` 或其他调用时 `isDebug = false`
398
+ - 插件可据此返回更详细的错误信息、调试日志等
399
+
384
400
  ## 错误类型
385
401
 
386
402
  | 错误 | 描述 |
package/dist/index.cjs CHANGED
@@ -217,6 +217,28 @@ var import_common3 = require("@nestjs/common");
217
217
  var import_nestjs_common = require("@lark-apaas/nestjs-common");
218
218
  var fs = __toESM(require("fs"), 1);
219
219
  var path = __toESM(require("path"), 1);
220
+
221
+ // src/utils/log-utils.ts
222
+ var DEFAULT_MAX_LENGTH = 1e3;
223
+ function truncateString(str, maxLength) {
224
+ if (str.length <= maxLength) {
225
+ return str;
226
+ }
227
+ return str.slice(0, maxLength) + `... [truncated, total ${str.length} chars]`;
228
+ }
229
+ __name(truncateString, "truncateString");
230
+ function stringifyForLog(value, maxLength = DEFAULT_MAX_LENGTH) {
231
+ try {
232
+ const str = JSON.stringify(value, null, 2);
233
+ return truncateString(str, maxLength);
234
+ } catch {
235
+ const str = String(value);
236
+ return truncateString(str, maxLength);
237
+ }
238
+ }
239
+ __name(stringifyForLog, "stringifyForLog");
240
+
241
+ // src/services/capability.service.ts
220
242
  function _ts_decorate3(decorators, target, key, desc) {
221
243
  var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
222
244
  if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
@@ -257,15 +279,15 @@ var CapabilityService = class _CapabilityService {
257
279
  __name(this, "CapabilityService");
258
280
  }
259
281
  requestContextService;
260
- httpClient;
282
+ platformHttpClient;
261
283
  pluginLoaderService;
262
284
  templateEngineService;
263
285
  logger = new import_common3.Logger(_CapabilityService.name);
264
286
  capabilities = /* @__PURE__ */ new Map();
265
287
  capabilitiesDir;
266
- constructor(requestContextService, httpClient, pluginLoaderService, templateEngineService) {
288
+ constructor(requestContextService, platformHttpClient, pluginLoaderService, templateEngineService) {
267
289
  this.requestContextService = requestContextService;
268
- this.httpClient = httpClient;
290
+ this.platformHttpClient = platformHttpClient;
269
291
  this.pluginLoaderService = pluginLoaderService;
270
292
  this.templateEngineService = templateEngineService;
271
293
  this.capabilitiesDir = path.join(process.cwd(), "server/capabilities");
@@ -353,20 +375,23 @@ var CapabilityService = class _CapabilityService {
353
375
  */
354
376
  async executeCall(config, actionName, input, contextOverride) {
355
377
  const startTime = Date.now();
378
+ const loggerContext = {
379
+ capability_id: config.id,
380
+ plugin_key: config.pluginKey,
381
+ action: actionName
382
+ };
356
383
  try {
357
384
  const pluginInstance = await this.pluginLoaderService.loadPlugin(config.pluginKey);
358
385
  if (!pluginInstance.hasAction(actionName)) {
359
386
  throw new ActionNotFoundError(config.pluginKey, actionName);
360
387
  }
361
- const resolvedParams = this.templateEngineService.resolve(config.formValue, input);
388
+ const resolvedParams = config.formValue ? this.templateEngineService.resolve(config.formValue, input) : input;
362
389
  const context = this.buildActionContext(contextOverride);
363
390
  const isStream = pluginInstance.isStreamAction?.(actionName) ?? false;
364
- this.logger.log({
365
- message: "Executing capability",
366
- capabilityId: config.id,
367
- action: actionName,
368
- pluginKey: config.pluginKey,
369
- isStream
391
+ this.logger.log("Executing capability (call)", {
392
+ ...loggerContext,
393
+ is_stream: isStream,
394
+ input: stringifyForLog(input)
370
395
  });
371
396
  let result;
372
397
  if (isStream && pluginInstance.runStream) {
@@ -378,20 +403,17 @@ var CapabilityService = class _CapabilityService {
378
403
  } else {
379
404
  result = await pluginInstance.run(actionName, context, resolvedParams);
380
405
  }
381
- this.logger.log({
382
- message: "Capability executed successfully",
383
- capabilityId: config.id,
384
- action: actionName,
385
- duration: Date.now() - startTime
406
+ this.logger.log("Capability (call) executed successfully", {
407
+ ...loggerContext,
408
+ duration_ms: Date.now() - startTime,
409
+ output: stringifyForLog(result)
386
410
  });
387
411
  return result;
388
412
  } catch (error) {
389
- this.logger.error({
390
- message: "Capability execution failed",
391
- capabilityId: config.id,
392
- action: actionName,
393
- error: error instanceof Error ? error.message : String(error),
394
- duration: Date.now() - startTime
413
+ this.logger.error("Capability (call) execution failed", {
414
+ ...loggerContext,
415
+ duration_ms: Date.now() - startTime,
416
+ error: error instanceof Error ? error.message : String(error)
395
417
  });
396
418
  throw error;
397
419
  }
@@ -403,40 +425,46 @@ var CapabilityService = class _CapabilityService {
403
425
  */
404
426
  async *executeCallStream(config, actionName, input, contextOverride) {
405
427
  const startTime = Date.now();
428
+ const loggerContext = {
429
+ capability_id: config.id,
430
+ plugin_key: config.pluginKey,
431
+ action: actionName
432
+ };
433
+ const chunks = [];
406
434
  try {
407
435
  const pluginInstance = await this.pluginLoaderService.loadPlugin(config.pluginKey);
408
436
  if (!pluginInstance.hasAction(actionName)) {
409
437
  throw new ActionNotFoundError(config.pluginKey, actionName);
410
438
  }
411
- const resolvedParams = this.templateEngineService.resolve(config.formValue, input);
439
+ const resolvedParams = config.formValue ? this.templateEngineService.resolve(config.formValue, input) : input;
412
440
  const context = this.buildActionContext(contextOverride);
413
441
  const isStream = pluginInstance.isStreamAction?.(actionName) ?? false;
414
- this.logger.log({
415
- message: "Executing capability (stream)",
416
- capabilityId: config.id,
417
- action: actionName,
418
- pluginKey: config.pluginKey,
419
- isStream
442
+ this.logger.log("Executing capability (stream)", {
443
+ ...loggerContext,
444
+ is_stream: isStream,
445
+ input: stringifyForLog(input)
420
446
  });
421
447
  if (isStream && pluginInstance.runStream) {
422
- yield* pluginInstance.runStream(actionName, context, resolvedParams);
448
+ for await (const chunk of pluginInstance.runStream(actionName, context, resolvedParams)) {
449
+ chunks.push(chunk);
450
+ yield chunk;
451
+ }
423
452
  } else {
424
453
  const result = await pluginInstance.run(actionName, context, resolvedParams);
454
+ chunks.push(result);
425
455
  yield result;
426
456
  }
427
- this.logger.log({
428
- message: "Capability stream completed",
429
- capabilityId: config.id,
430
- action: actionName,
431
- duration: Date.now() - startTime
457
+ const aggregatedResult = pluginInstance.aggregate ? pluginInstance.aggregate(actionName, chunks) : chunks;
458
+ this.logger.log("Capability (stream) executed successfully", {
459
+ ...loggerContext,
460
+ duration_ms: Date.now() - startTime,
461
+ output: stringifyForLog(aggregatedResult)
432
462
  });
433
463
  } catch (error) {
434
- this.logger.error({
435
- message: "Capability stream execution failed",
436
- capabilityId: config.id,
437
- action: actionName,
438
- error: error instanceof Error ? error.message : String(error),
439
- duration: Date.now() - startTime
464
+ this.logger.error("Capability (stream) execution failed", {
465
+ ...loggerContext,
466
+ duration_ms: Date.now() - startTime,
467
+ error: error instanceof Error ? error.message : String(error)
440
468
  });
441
469
  throw error;
442
470
  }
@@ -448,27 +476,47 @@ var CapabilityService = class _CapabilityService {
448
476
  */
449
477
  async *executeCallStreamWithEvents(config, actionName, input, contextOverride) {
450
478
  const startTime = Date.now();
451
- let chunkCount = 0;
479
+ const loggerContext = {
480
+ capability_id: config.id,
481
+ plugin_key: config.pluginKey,
482
+ action: actionName
483
+ };
484
+ const chunks = [];
452
485
  try {
453
486
  const pluginInstance = await this.pluginLoaderService.loadPlugin(config.pluginKey);
454
487
  if (!pluginInstance.hasAction(actionName)) {
455
488
  throw new ActionNotFoundError(config.pluginKey, actionName);
456
489
  }
457
- const resolvedParams = this.templateEngineService.resolve(config.formValue, input);
490
+ const resolvedParams = config.formValue ? this.templateEngineService.resolve(config.formValue, input) : input;
458
491
  const context = this.buildActionContext(contextOverride);
459
- this.logger.log({
460
- message: "Executing capability (streamWithEvents)",
461
- capabilityId: config.id,
462
- action: actionName,
463
- pluginKey: config.pluginKey
492
+ const isStream = pluginInstance.isStreamAction?.(actionName) ?? false;
493
+ this.logger.log("Executing capability (streamWithEvents)", {
494
+ ...loggerContext,
495
+ is_stream: isStream,
496
+ input: stringifyForLog(input)
464
497
  });
465
498
  if (pluginInstance.runStreamWithEvents) {
466
- yield* pluginInstance.runStreamWithEvents(actionName, context, resolvedParams);
499
+ for await (const event of pluginInstance.runStreamWithEvents(actionName, context, resolvedParams)) {
500
+ if (event.type === "data") {
501
+ chunks.push(event.data);
502
+ yield event;
503
+ } else if (event.type === "done") {
504
+ const aggregatedResult2 = pluginInstance.aggregate ? pluginInstance.aggregate(actionName, chunks) : chunks;
505
+ yield {
506
+ type: "done",
507
+ metadata: {
508
+ ...event.metadata,
509
+ aggregated: aggregatedResult2
510
+ }
511
+ };
512
+ } else {
513
+ yield event;
514
+ }
515
+ }
467
516
  } else {
468
- const isStream = pluginInstance.isStreamAction?.(actionName) ?? false;
469
517
  if (isStream && pluginInstance.runStream) {
470
518
  for await (const chunk of pluginInstance.runStream(actionName, context, resolvedParams)) {
471
- chunkCount++;
519
+ chunks.push(chunk);
472
520
  yield {
473
521
  type: "data",
474
522
  data: chunk
@@ -476,34 +524,33 @@ var CapabilityService = class _CapabilityService {
476
524
  }
477
525
  } else {
478
526
  const result = await pluginInstance.run(actionName, context, resolvedParams);
479
- chunkCount = 1;
527
+ chunks.push(result);
480
528
  yield {
481
529
  type: "data",
482
530
  data: result
483
531
  };
484
532
  }
533
+ const aggregatedResult2 = pluginInstance.aggregate ? pluginInstance.aggregate(actionName, chunks) : chunks;
485
534
  yield {
486
535
  type: "done",
487
536
  metadata: {
488
- chunks: chunkCount,
489
- duration: Date.now() - startTime
537
+ chunks: chunks.length,
538
+ duration: Date.now() - startTime,
539
+ aggregated: aggregatedResult2
490
540
  }
491
541
  };
492
542
  }
493
- this.logger.log({
494
- message: "Capability streamWithEvents completed",
495
- capabilityId: config.id,
496
- action: actionName,
497
- duration: Date.now() - startTime,
498
- chunks: chunkCount
543
+ const aggregatedResult = pluginInstance.aggregate ? pluginInstance.aggregate(actionName, chunks) : chunks;
544
+ this.logger.log("Capability (streamWithEvents) executed successfully", {
545
+ ...loggerContext,
546
+ duration_ms: Date.now() - startTime,
547
+ output: stringifyForLog(aggregatedResult)
499
548
  });
500
549
  } catch (error) {
501
- this.logger.error({
502
- message: "Capability streamWithEvents execution failed",
503
- capabilityId: config.id,
504
- action: actionName,
505
- error: error instanceof Error ? error.message : String(error),
506
- duration: Date.now() - startTime
550
+ this.logger.error("Capability (streamWithEvents) execution failed", {
551
+ ...loggerContext,
552
+ duration_ms: Date.now() - startTime,
553
+ error: error instanceof Error ? error.message : String(error)
507
554
  });
508
555
  yield {
509
556
  type: "error",
@@ -517,13 +564,15 @@ var CapabilityService = class _CapabilityService {
517
564
  buildActionContext(override) {
518
565
  return {
519
566
  logger: this.logger,
520
- httpClient: this.httpClient,
521
- userContext: override?.userContext ?? this.getUserContext()
567
+ platformHttpClient: this.platformHttpClient,
568
+ userContext: override?.userContext ?? this.getUserContext(),
569
+ isDebug: override?.isDebug ?? false
522
570
  };
523
571
  }
524
572
  getUserContext() {
525
573
  const ctx = this.requestContextService.getContext();
526
574
  return {
575
+ appId: ctx?.appId ?? "",
527
576
  userId: ctx?.userId ?? "",
528
577
  tenantId: ctx?.tenantId ?? ""
529
578
  };
@@ -560,13 +609,14 @@ function _ts_param2(paramIndex, decorator) {
560
609
  };
561
610
  }
562
611
  __name(_ts_param2, "_ts_param");
563
- var DebugController = class {
612
+ var DebugController = class _DebugController {
564
613
  static {
565
614
  __name(this, "DebugController");
566
615
  }
567
616
  capabilityService;
568
617
  pluginLoaderService;
569
618
  templateEngineService;
619
+ logger = new import_common4.Logger(_DebugController.name);
570
620
  constructor(capabilityService, pluginLoaderService, templateEngineService) {
571
621
  this.capabilityService = capabilityService;
572
622
  this.pluginLoaderService = pluginLoaderService;
@@ -630,7 +680,9 @@ var DebugController = class {
630
680
  const action = await this.getActionName(config.pluginKey, body.action);
631
681
  const resolvedParams = this.templateEngineService.resolve(config.formValue, params);
632
682
  try {
633
- const result = await this.capabilityService.loadWithConfig(config).call(action, params);
683
+ const result = await this.capabilityService.loadWithConfig(config).call(action, params, {
684
+ isDebug: true
685
+ });
634
686
  return {
635
687
  status_code: ErrorCodes.SUCCESS,
636
688
  data: {
@@ -677,39 +729,61 @@ var DebugController = class {
677
729
  try {
678
730
  const config = this.getCapabilityConfig(capabilityId, body.capability);
679
731
  const action = await this.getActionName(config.pluginKey, body.action);
732
+ const loggerContext = {
733
+ capability_id: config.id,
734
+ plugin_key: config.pluginKey,
735
+ action
736
+ };
737
+ this.logger.log(`Executing capability (stream)`, {
738
+ ...loggerContext,
739
+ input: stringifyForLog(params)
740
+ });
680
741
  const capability = this.capabilityService.loadWithConfig(config);
681
- const eventStream = capability.callStreamWithEvents(action, params);
742
+ const eventStream = capability.callStreamWithEvents(action, params, {
743
+ isDebug: true
744
+ });
745
+ let pendingChunk = null;
682
746
  for await (const event of eventStream) {
683
747
  switch (event.type) {
684
748
  case "data": {
685
- const response = {
686
- status_code: ErrorCodes.SUCCESS,
687
- data: {
688
- type: "content",
689
- delta: {
690
- content: event.data
749
+ if (pendingChunk !== null) {
750
+ const response = {
751
+ status_code: ErrorCodes.SUCCESS,
752
+ data: {
753
+ type: "content",
754
+ delta: {
755
+ content: pendingChunk
756
+ }
691
757
  }
692
- }
693
- };
694
- res.write(`data: ${JSON.stringify(response)}
758
+ };
759
+ res.write(`data: ${JSON.stringify(response)}
695
760
 
696
761
  `);
762
+ }
763
+ pendingChunk = event.data;
697
764
  break;
698
765
  }
699
766
  case "done": {
700
- const response = {
701
- status_code: ErrorCodes.SUCCESS,
702
- data: {
703
- type: "content",
704
- delta: {
705
- content: null
706
- },
707
- finished: true
708
- }
709
- };
710
- res.write(`data: ${JSON.stringify(response)}
767
+ if (pendingChunk !== null) {
768
+ const response = {
769
+ status_code: ErrorCodes.SUCCESS,
770
+ data: {
771
+ type: "content",
772
+ delta: {
773
+ content: pendingChunk
774
+ },
775
+ finished: true
776
+ }
777
+ };
778
+ res.write(`data: ${JSON.stringify(response)}
711
779
 
712
780
  `);
781
+ }
782
+ this.logger.log(`Capability (stream) executed successfully`, {
783
+ ...loggerContext,
784
+ duration_ms: event.metadata.duration,
785
+ output: stringifyForLog(event.metadata.aggregated)
786
+ });
713
787
  res.end();
714
788
  return;
715
789
  }
@@ -732,6 +806,21 @@ var DebugController = class {
732
806
  }
733
807
  }
734
808
  }
809
+ if (pendingChunk !== null) {
810
+ const response = {
811
+ status_code: ErrorCodes.SUCCESS,
812
+ data: {
813
+ type: "content",
814
+ delta: {
815
+ content: pendingChunk
816
+ },
817
+ finished: true
818
+ }
819
+ };
820
+ res.write(`data: ${JSON.stringify(response)}
821
+
822
+ `);
823
+ }
735
824
  res.end();
736
825
  } catch (error) {
737
826
  const errorMsg = error instanceof Error ? error.message : String(error);
@@ -811,11 +900,12 @@ function _ts_param3(paramIndex, decorator) {
811
900
  };
812
901
  }
813
902
  __name(_ts_param3, "_ts_param");
814
- var WebhookController = class {
903
+ var WebhookController = class _WebhookController {
815
904
  static {
816
905
  __name(this, "WebhookController");
817
906
  }
818
907
  capabilityService;
908
+ logger = new import_common5.Logger(_WebhookController.name);
819
909
  constructor(capabilityService) {
820
910
  this.capabilityService = capabilityService;
821
911
  }
@@ -868,43 +958,62 @@ var WebhookController = class {
868
958
  }
869
959
  }
870
960
  async executeStream(capabilityId, body, res) {
961
+ const loggerContext = {
962
+ capability_id: capabilityId,
963
+ action: body.action
964
+ };
871
965
  res.setHeader("Content-Type", "text/event-stream");
872
966
  res.setHeader("Cache-Control", "no-cache");
873
967
  res.setHeader("Connection", "keep-alive");
874
968
  try {
969
+ this.logger.log(`Executing capability (stream)`, {
970
+ ...loggerContext,
971
+ input: stringifyForLog(body.params)
972
+ });
875
973
  const capability = this.capabilityService.load(capabilityId);
876
974
  const eventStream = capability.callStreamWithEvents(body.action, body.params);
975
+ let pendingChunk = null;
877
976
  for await (const event of eventStream) {
878
977
  switch (event.type) {
879
978
  case "data": {
880
- const response = {
881
- status_code: ErrorCodes.SUCCESS,
882
- data: {
883
- type: "content",
884
- delta: {
885
- content: event.data
979
+ if (pendingChunk !== null) {
980
+ const response = {
981
+ status_code: ErrorCodes.SUCCESS,
982
+ data: {
983
+ type: "content",
984
+ delta: {
985
+ content: pendingChunk
986
+ }
886
987
  }
887
- }
888
- };
889
- res.write(`data: ${JSON.stringify(response)}
988
+ };
989
+ res.write(`data: ${JSON.stringify(response)}
890
990
 
891
991
  `);
992
+ }
993
+ pendingChunk = event.data;
892
994
  break;
893
995
  }
894
996
  case "done": {
895
- const response = {
896
- status_code: ErrorCodes.SUCCESS,
897
- data: {
898
- type: "content",
899
- delta: {
900
- content: null
901
- },
902
- finished: true
903
- }
904
- };
905
- res.write(`data: ${JSON.stringify(response)}
997
+ if (pendingChunk !== null) {
998
+ const response = {
999
+ status_code: ErrorCodes.SUCCESS,
1000
+ data: {
1001
+ type: "content",
1002
+ delta: {
1003
+ content: pendingChunk
1004
+ },
1005
+ finished: true
1006
+ }
1007
+ };
1008
+ res.write(`data: ${JSON.stringify(response)}
906
1009
 
907
1010
  `);
1011
+ }
1012
+ this.logger.log(`Capability (stream) executed successfully`, {
1013
+ ...loggerContext,
1014
+ duration_ms: event.metadata.duration,
1015
+ output: stringifyForLog(event.metadata.aggregated)
1016
+ });
908
1017
  res.end();
909
1018
  return;
910
1019
  }
@@ -927,6 +1036,21 @@ var WebhookController = class {
927
1036
  }
928
1037
  }
929
1038
  }
1039
+ if (pendingChunk !== null) {
1040
+ const response = {
1041
+ status_code: ErrorCodes.SUCCESS,
1042
+ data: {
1043
+ type: "content",
1044
+ delta: {
1045
+ content: pendingChunk
1046
+ },
1047
+ finished: true
1048
+ }
1049
+ };
1050
+ res.write(`data: ${JSON.stringify(response)}
1051
+
1052
+ `);
1053
+ }
930
1054
  res.end();
931
1055
  } catch (error) {
932
1056
  const errorMsg = error instanceof Error ? error.message : String(error);
@@ -1014,6 +1138,9 @@ var CapabilityModule = class _CapabilityModule {
1014
1138
  static forRoot(options) {
1015
1139
  return {
1016
1140
  module: _CapabilityModule,
1141
+ imports: [
1142
+ import_nestjs_common2.CommonModule
1143
+ ],
1017
1144
  controllers: getControllers(),
1018
1145
  providers: [
1019
1146
  {
@@ -1048,6 +1175,9 @@ var CapabilityModule = class _CapabilityModule {
1048
1175
  };
1049
1176
  CapabilityModule = _ts_decorate6([
1050
1177
  (0, import_common6.Module)({
1178
+ imports: [
1179
+ import_nestjs_common2.CommonModule
1180
+ ],
1051
1181
  controllers: getControllers(),
1052
1182
  providers: [
1053
1183
  CapabilityService,