@slates/provider-handler 1.0.0-rc.2 → 1.0.0-rc.20

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.
Files changed (67) hide show
  1. package/dist/index.cjs +1140 -2
  2. package/dist/index.d.cts +8 -0
  3. package/dist/index.d.ts +6 -3
  4. package/dist/index.module.js +1115 -2
  5. package/package.json +11 -11
  6. package/src/index.ts +692 -114
  7. package/src/spec.ts +54 -16
  8. package/src/validation.ts +17 -1
  9. package/src/webhook.test.ts +65 -0
  10. package/src/webhook.ts +69 -0
  11. package/dist/action/action.d.ts +0 -90
  12. package/dist/action/action.d.ts.map +0 -1
  13. package/dist/action/builder.d.ts +0 -27
  14. package/dist/action/builder.d.ts.map +0 -1
  15. package/dist/action/index.d.ts +0 -5
  16. package/dist/action/index.d.ts.map +0 -1
  17. package/dist/action/tool.d.ts +0 -11
  18. package/dist/action/tool.d.ts.map +0 -1
  19. package/dist/action/trigger.d.ts +0 -14
  20. package/dist/action/trigger.d.ts.map +0 -1
  21. package/dist/auth/auth.d.ts +0 -26
  22. package/dist/auth/auth.d.ts.map +0 -1
  23. package/dist/auth/index.d.ts +0 -3
  24. package/dist/auth/index.d.ts.map +0 -1
  25. package/dist/auth/types.d.ts +0 -148
  26. package/dist/auth/types.d.ts.map +0 -1
  27. package/dist/axios/index.d.ts +0 -4
  28. package/dist/axios/index.d.ts.map +0 -1
  29. package/dist/config/config.d.ts +0 -22
  30. package/dist/config/config.d.ts.map +0 -1
  31. package/dist/config/index.d.ts +0 -2
  32. package/dist/config/index.d.ts.map +0 -1
  33. package/dist/context/context.d.ts +0 -20
  34. package/dist/context/context.d.ts.map +0 -1
  35. package/dist/context/hook.d.ts +0 -4
  36. package/dist/context/hook.d.ts.map +0 -1
  37. package/dist/context/index.d.ts +0 -2
  38. package/dist/context/index.d.ts.map +0 -1
  39. package/dist/error/base.d.ts +0 -5
  40. package/dist/error/base.d.ts.map +0 -1
  41. package/dist/error/declaration.d.ts +0 -6
  42. package/dist/error/declaration.d.ts.map +0 -1
  43. package/dist/error/index.d.ts +0 -3
  44. package/dist/error/index.d.ts.map +0 -1
  45. package/dist/index.cjs.map +0 -1
  46. package/dist/index.d.ts.map +0 -1
  47. package/dist/index.modern.js +0 -2
  48. package/dist/index.modern.js.map +0 -1
  49. package/dist/index.module.js.map +0 -1
  50. package/dist/index.umd.js +0 -2
  51. package/dist/index.umd.js.map +0 -1
  52. package/dist/spec.d.ts +0 -14
  53. package/dist/spec.d.ts.map +0 -1
  54. package/dist/specification/index.d.ts +0 -3
  55. package/dist/specification/index.d.ts.map +0 -1
  56. package/dist/specification/slate.d.ts +0 -15
  57. package/dist/specification/slate.d.ts.map +0 -1
  58. package/dist/specification/specification.d.ts +0 -30
  59. package/dist/specification/specification.d.ts.map +0 -1
  60. package/dist/specification/zero.d.ts +0 -13
  61. package/dist/specification/zero.d.ts.map +0 -1
  62. package/dist/state.d.ts +0 -8
  63. package/dist/state.d.ts.map +0 -1
  64. package/dist/tokens.d.ts +0 -34
  65. package/dist/tokens.d.ts.map +0 -1
  66. package/dist/validation.d.ts +0 -4
  67. package/dist/validation.d.ts.map +0 -1
package/src/index.ts CHANGED
@@ -1,9 +1,120 @@
1
1
  import { badRequestError, preconditionFailedError, ServiceError } from '@lowerdeck/error';
2
- import { createSlatesProviderProtoHandler, SlatesParticipant } from '@slates/proto';
3
- import { Slate, SlateContext, SlateLogger, SlateLogListener } from '@slates/provider';
4
- import { getAction, getActionWithType, getAuthMethod, mapAction, mapAuthMethod } from './spec';
2
+ import {
3
+ createSlatesProviderProtoHandler,
4
+ SLATES_PROTOCOL_VERSION,
5
+ type SlatesParticipant
6
+ } from '@slates/proto';
7
+ import {
8
+ runWithContext,
9
+ type Slate,
10
+ type SlateAttachment,
11
+ SlateContext,
12
+ SlateLogger,
13
+ type SlateLogListener,
14
+ SlatePublicContext
15
+ } from '@slates/provider';
16
+ import {
17
+ getAction,
18
+ getActionWithType,
19
+ getAdapter,
20
+ getAuthMethod,
21
+ mapAction,
22
+ mapAdapter,
23
+ mapAuthMethod
24
+ } from './spec';
5
25
  import { State } from './state';
6
- import { validate } from './validation';
26
+ import { toJsonSchema, validate } from './validation';
27
+ import { serializeWebhookHttpResponse } from './webhook';
28
+
29
+ let isRecord = (value: unknown): value is Record<string, unknown> =>
30
+ typeof value === 'object' && value !== null && !Array.isArray(value);
31
+
32
+ let getObjectKeyCount = (value: unknown) =>
33
+ isRecord(value) ? Object.keys(value).length : undefined;
34
+
35
+ let DOWNLOAD_ATTACHMENT_URL_KEYS = new Set([
36
+ 'downloadUrl',
37
+ 'fileUrl',
38
+ 'temporaryDownloadUrl',
39
+ 'webContentLink'
40
+ ]);
41
+
42
+ let isAttachmentUrl = (value: string) => /^[a-z][a-z0-9+.-]*:\/\//i.test(value);
43
+
44
+ let collectOutputUrlAttachments = (
45
+ value: unknown,
46
+ seen = new Set<string>()
47
+ ): SlateAttachment[] => {
48
+ if (Array.isArray(value)) {
49
+ return value.flatMap(item => collectOutputUrlAttachments(item, seen));
50
+ }
51
+
52
+ if (!isRecord(value)) {
53
+ return [];
54
+ }
55
+
56
+ let attachments: SlateAttachment[] = [];
57
+
58
+ for (let [key, nestedValue] of Object.entries(value)) {
59
+ if (
60
+ DOWNLOAD_ATTACHMENT_URL_KEYS.has(key) &&
61
+ typeof nestedValue === 'string' &&
62
+ nestedValue.length > 0 &&
63
+ isAttachmentUrl(nestedValue) &&
64
+ !seen.has(nestedValue)
65
+ ) {
66
+ seen.add(nestedValue);
67
+ attachments.push({
68
+ content: {
69
+ type: 'url',
70
+ url: nestedValue
71
+ }
72
+ });
73
+ continue;
74
+ }
75
+
76
+ attachments.push(...collectOutputUrlAttachments(nestedValue, seen));
77
+ }
78
+
79
+ return attachments;
80
+ };
81
+
82
+ let mergeAttachments = (
83
+ explicitAttachments: SlateAttachment[] | undefined,
84
+ output: unknown
85
+ ): SlateAttachment[] | undefined => {
86
+ let attachments = [...(explicitAttachments ?? [])];
87
+ let seen = new Set(attachments.map(attachment => JSON.stringify(attachment)));
88
+
89
+ for (let attachment of collectOutputUrlAttachments(output)) {
90
+ let key = JSON.stringify(attachment);
91
+ if (seen.has(key)) continue;
92
+ seen.add(key);
93
+ attachments.push(attachment);
94
+ }
95
+
96
+ return attachments.length > 0 ? attachments : undefined;
97
+ };
98
+
99
+ let toErrorMetadata = (error: unknown) => {
100
+ if (error instanceof Error) {
101
+ return {
102
+ errorName: error.name,
103
+ errorMessage: error.message,
104
+ errorStack: error.stack
105
+ };
106
+ }
107
+
108
+ return {
109
+ errorValue: String(error)
110
+ };
111
+ };
112
+
113
+ let formatEntityLabel = (name: string, key: string) => `"${name}" (${key})`;
114
+ let resolveTraceMessage = <ResultType>(
115
+ message: string | ((result: ResultType) => string),
116
+ result: ResultType
117
+ ) => (typeof message === 'function' ? message(result) : message);
7
118
 
8
119
  export let createProviderHandler = <ConfigType extends {}, AuthType extends {}>(
9
120
  slate: Slate<ConfigType, AuthType>,
@@ -15,10 +126,69 @@ export let createProviderHandler = <ConfigType extends {}, AuthType extends {}>(
15
126
 
16
127
  let auth = new State<{ authenticationMethodId: string; output: AuthType } | null>(null);
17
128
  let config = new State<{ value: ConfigType } | null>(null);
18
-
19
129
  let session = new State<{ id: string; state: any } | null>(null);
20
130
 
21
131
  let logger = new SlateLogger(listeners);
132
+ let providerTrace = {
133
+ providerId: slate.spec.key,
134
+ providerName: slate.spec.name
135
+ };
136
+
137
+ let traceProviderCall = async <ResultType>(
138
+ trace: {
139
+ component: 'config' | 'auth' | 'action';
140
+ functionName: string;
141
+ message: string;
142
+ successMessage: string | ((result: ResultType) => string);
143
+ errorMessage?: string;
144
+ metadata?: Record<string, unknown>;
145
+ onSuccess?: (result: ResultType) => Record<string, unknown> | undefined;
146
+ },
147
+ handler: () => Promise<ResultType>
148
+ ): Promise<ResultType> => {
149
+ let startedAt = Date.now();
150
+
151
+ logger.info({
152
+ ...providerTrace,
153
+ ...trace.metadata,
154
+ component: trace.component,
155
+ functionName: trace.functionName,
156
+ phase: 'start',
157
+ message: trace.message
158
+ });
159
+
160
+ try {
161
+ let result = await handler();
162
+ let successMessage = resolveTraceMessage(trace.successMessage, result);
163
+
164
+ logger.info({
165
+ ...providerTrace,
166
+ ...trace.metadata,
167
+ ...(trace.onSuccess?.(result) ?? {}),
168
+ component: trace.component,
169
+ functionName: trace.functionName,
170
+ phase: 'success',
171
+ durationMs: Date.now() - startedAt,
172
+ message: successMessage
173
+ });
174
+
175
+ return result;
176
+ } catch (error) {
177
+ logger.error({
178
+ ...providerTrace,
179
+ ...trace.metadata,
180
+ ...toErrorMetadata(error),
181
+ component: trace.component,
182
+ functionName: trace.functionName,
183
+ phase: 'error',
184
+ durationMs: Date.now() - startedAt,
185
+ message:
186
+ trace.errorMessage ??
187
+ `${typeof trace.successMessage === 'string' ? trace.successMessage : trace.message} failed`
188
+ });
189
+ throw error;
190
+ }
191
+ };
22
192
 
23
193
  let getContextBasic = () => {
24
194
  let currentProtocol = protocol.get();
@@ -65,6 +235,19 @@ export let createProviderHandler = <ConfigType extends {}, AuthType extends {}>(
65
235
  };
66
236
  };
67
237
 
238
+ let getAuthConfig = (): Record<string, any> => config.get()?.value ?? {};
239
+
240
+ let getEmptyContext = () => new SlateContext({}, {}, {}, slate.spec as any, logger);
241
+ let getAuthContext = () =>
242
+ new SlateContext(getAuthConfig(), {}, {}, slate.spec as any, logger);
243
+ let withRequestTraces = <Result extends Record<string, any>>(
244
+ context: SlatePublicContext<any>,
245
+ result: Result
246
+ ) => {
247
+ let requestTraces = context.getHttpTraces();
248
+ return requestTraces.length > 0 ? { ...result, requestTraces } : result;
249
+ };
250
+
68
251
  manager.onNotification('slates/hello', async ({ params }) => {
69
252
  protocol.set(params.protocol);
70
253
  });
@@ -133,15 +316,37 @@ export let createProviderHandler = <ConfigType extends {}, AuthType extends {}>(
133
316
  return { success: true, config: newConfig };
134
317
  }
135
318
 
136
- let updatedConfig = await configChanged({
137
- previousConfig: params.previousConfig as ConfigType | null,
138
- newConfig
139
- });
319
+ let context = getEmptyContext();
320
+ let updatedConfig = await traceProviderCall<{ config?: ConfigType } | undefined>(
321
+ {
322
+ component: 'config',
323
+ functionName: 'configChanged',
324
+ message: 'Running config change handler',
325
+ successMessage: 'Config change handler completed',
326
+ metadata: {
327
+ hasPreviousConfig: params.previousConfig !== null,
328
+ newConfigKeyCount: getObjectKeyCount(newConfig)
329
+ },
330
+ onSuccess: result => ({
331
+ returnedConfig: !!result?.config
332
+ })
333
+ },
334
+ () =>
335
+ runWithContext(context, async () =>
336
+ configChanged({
337
+ previousConfig: params.previousConfig as ConfigType | null,
338
+ newConfig
339
+ })
340
+ )
341
+ );
140
342
 
141
- return { success: true, config: updatedConfig?.config ?? newConfig };
343
+ return withRequestTraces(context, {
344
+ success: true,
345
+ config: (updatedConfig?.config ?? newConfig) as Record<string, any>
346
+ });
142
347
  });
143
348
 
144
- manager.onRequest('slates/config.get_default', async ({ params }) => {
349
+ manager.onRequest('slates/config.get_default', async () => {
145
350
  getContextBasic();
146
351
 
147
352
  let getDefaultConfig = slate.spec.config.handlers.getDefaultConfig;
@@ -149,32 +354,50 @@ export let createProviderHandler = <ConfigType extends {}, AuthType extends {}>(
149
354
  return { config: null };
150
355
  }
151
356
 
152
- let defaultConfig = await getDefaultConfig();
153
- return { config: defaultConfig };
357
+ let context = getEmptyContext();
358
+ let defaultConfig = await traceProviderCall<ConfigType>(
359
+ {
360
+ component: 'config',
361
+ functionName: 'getDefaultConfig',
362
+ message: 'Getting default config',
363
+ successMessage: 'Default config retrieved',
364
+ onSuccess: result => ({
365
+ configKeyCount: getObjectKeyCount(result)
366
+ })
367
+ },
368
+ () => runWithContext(context, async () => getDefaultConfig())
369
+ );
370
+ return withRequestTraces(context, {
371
+ config: (defaultConfig ?? null) as Record<string, any> | null
372
+ });
154
373
  });
155
374
 
156
- manager.onRequest('slates/config.schema.get', async ({ params }) => {
375
+ manager.onRequest('slates/config.schema.get', async () => {
157
376
  getContextBasic();
158
377
 
159
- return { schema: slate.spec.configSchema.toJSONSchema() };
378
+ return {
379
+ schema: toJsonSchema(slate.spec.configSchema),
380
+ docs: slate.spec.config.docsReferences ?? []
381
+ };
160
382
  });
161
383
 
162
- manager.onRequest('slates/provider.identify', async ({ params }) => {
384
+ manager.onRequest('slates/provider.identify', async () => {
163
385
  getContextBasic();
164
386
 
165
387
  return {
166
- protocol: 'slates@2026-01-01',
388
+ protocol: SLATES_PROTOCOL_VERSION,
167
389
  provider: {
168
390
  type: 'provider',
169
391
  id: slate.spec.key,
170
392
  name: slate.spec.name,
171
393
  description: slate.spec.description,
172
394
  metadata: slate.spec.parameters.metadata
173
- }
395
+ },
396
+ docs: slate.spec.docs ?? []
174
397
  };
175
398
  });
176
399
 
177
- manager.onRequest('slates/auth.methods.list', async ({ params }) => {
400
+ manager.onRequest('slates/auth.methods.list', async () => {
178
401
  getContextBasic();
179
402
 
180
403
  return {
@@ -199,7 +422,25 @@ export let createProviderHandler = <ConfigType extends {}, AuthType extends {}>(
199
422
  return { input: null };
200
423
  }
201
424
 
202
- return { input: await authMethod.getDefaultInput() };
425
+ let context = getEmptyContext();
426
+ let input = await traceProviderCall(
427
+ {
428
+ component: 'auth',
429
+ functionName: 'getDefaultInput',
430
+ message: 'Getting default authentication input',
431
+ successMessage: 'Default authentication input retrieved',
432
+ metadata: {
433
+ authenticationMethodId: params.authenticationMethodId,
434
+ authenticationMethodName: authMethod.name
435
+ },
436
+ onSuccess: result => ({
437
+ inputKeyCount: getObjectKeyCount(result)
438
+ })
439
+ },
440
+ () => runWithContext(context, () => authMethod.getDefaultInput!())
441
+ );
442
+
443
+ return withRequestTraces(context, { input });
203
444
  });
204
445
 
205
446
  manager.onRequest('slates/auth.input.changed', async ({ params }) => {
@@ -210,12 +451,36 @@ export let createProviderHandler = <ConfigType extends {}, AuthType extends {}>(
210
451
  return { success: true, input: params.newInput };
211
452
  }
212
453
 
213
- let updatedInput = await authMethod.onInputChanged({
214
- previousInput: params.previousInput as any | null,
215
- newInput: params.newInput
216
- });
454
+ let context = getEmptyContext();
455
+ let updatedInput = await traceProviderCall(
456
+ {
457
+ component: 'auth',
458
+ functionName: 'onInputChanged',
459
+ message: 'Running authentication input change handler',
460
+ successMessage: 'Authentication input change handler completed',
461
+ metadata: {
462
+ authenticationMethodId: params.authenticationMethodId,
463
+ authenticationMethodName: authMethod.name,
464
+ hasPreviousInput: params.previousInput !== null,
465
+ newInputKeyCount: getObjectKeyCount(params.newInput)
466
+ },
467
+ onSuccess: result => ({
468
+ returnedInput: !!result?.input
469
+ })
470
+ },
471
+ () =>
472
+ runWithContext(context, () =>
473
+ authMethod.onInputChanged!({
474
+ previousInput: params.previousInput as any | null,
475
+ newInput: params.newInput
476
+ })
477
+ )
478
+ );
217
479
 
218
- return { success: true, input: updatedInput?.input ?? params.newInput };
480
+ return withRequestTraces(context, {
481
+ success: true,
482
+ input: updatedInput?.input ?? params.newInput
483
+ });
219
484
  });
220
485
 
221
486
  manager.onRequest('slates/auth.output.get', async ({ params }) => {
@@ -234,8 +499,35 @@ export let createProviderHandler = <ConfigType extends {}, AuthType extends {}>(
234
499
  }
235
500
 
236
501
  if ('getOutput' in authMethod) {
237
- let outputRes = await authMethod.getOutput({ input });
238
- return { output: outputRes.output };
502
+ let context = getAuthContext();
503
+ let outputRes = await traceProviderCall(
504
+ {
505
+ component: 'auth',
506
+ functionName: 'getOutput',
507
+ message: 'Getting authentication output',
508
+ successMessage: 'Authentication output retrieved',
509
+ metadata: {
510
+ authenticationMethodId: params.authenticationMethodId,
511
+ authenticationMethodName: authMethod.name,
512
+ inputKeyCount: getObjectKeyCount(input)
513
+ },
514
+ onSuccess: result => ({
515
+ outputKeyCount: getObjectKeyCount(result.output),
516
+ scopeCount: result.scopes?.length ?? 0
517
+ })
518
+ },
519
+ () =>
520
+ runWithContext(context, () =>
521
+ authMethod.getOutput({
522
+ input,
523
+ config: getAuthConfig()
524
+ })
525
+ )
526
+ );
527
+ return withRequestTraces(context, {
528
+ output: outputRes.output,
529
+ scopes: outputRes.scopes
530
+ });
239
531
  }
240
532
 
241
533
  return { output: input as any };
@@ -246,20 +538,47 @@ export let createProviderHandler = <ConfigType extends {}, AuthType extends {}>(
246
538
  let authMethod = getAuthMethod(slate, params.authenticationMethodId);
247
539
 
248
540
  if ('handleCallback' in authMethod) {
249
- let callbackRes = await authMethod.handleCallback({
250
- code: params.code,
251
- state: params.state,
252
- redirectUri: params.redirectUri,
253
- input: params.input,
254
- clientId: params.clientId,
255
- clientSecret: params.clientSecret,
256
- scopes: params.scopes
257
- });
541
+ let context = getEmptyContext();
542
+ let callbackRes = await traceProviderCall(
543
+ {
544
+ component: 'auth',
545
+ functionName: 'handleCallback',
546
+ message: 'Handling authentication callback',
547
+ successMessage: 'Authentication callback handled',
548
+ metadata: {
549
+ authenticationMethodId: params.authenticationMethodId,
550
+ authenticationMethodName: authMethod.name,
551
+ scopeCount: params.scopes.length,
552
+ hasCallbackState: !!params.callbackState
553
+ },
554
+ onSuccess: result => ({
555
+ outputKeyCount: getObjectKeyCount(result.output),
556
+ returnedInput: !!result.input,
557
+ returnedScopeCount: result.scopes?.length
558
+ })
559
+ },
560
+ () =>
561
+ runWithContext(context, () =>
562
+ authMethod.handleCallback({
563
+ code: params.code,
564
+ state: params.state,
565
+ redirectUri: params.redirectUri,
566
+ input: params.input,
567
+ clientId: params.clientId,
568
+ clientSecret: params.clientSecret,
569
+ scopes: params.scopes,
570
+ callbackParams: params.callbackParams || {},
571
+ callbackState: params.callbackState || {},
572
+ config: getAuthConfig()
573
+ })
574
+ )
575
+ );
258
576
 
259
- return {
577
+ return withRequestTraces(context, {
260
578
  output: callbackRes.output,
261
- input: callbackRes.input
262
- };
579
+ input: callbackRes.input,
580
+ scopes: callbackRes.scopes
581
+ });
263
582
  }
264
583
 
265
584
  throw new ServiceError(
@@ -274,19 +593,43 @@ export let createProviderHandler = <ConfigType extends {}, AuthType extends {}>(
274
593
  let authMethod = getAuthMethod(slate, params.authenticationMethodId);
275
594
 
276
595
  if ('getAuthorizationUrl' in authMethod) {
277
- let urlRes = await authMethod.getAuthorizationUrl({
278
- redirectUri: params.redirectUri,
279
- state: params.state,
280
- input: params.input,
281
- clientId: params.clientId,
282
- clientSecret: params.clientSecret,
283
- scopes: params.scopes
284
- });
596
+ let context = getEmptyContext();
597
+ let urlRes = await traceProviderCall(
598
+ {
599
+ component: 'auth',
600
+ functionName: 'getAuthorizationUrl',
601
+ message: 'Getting authentication authorization URL',
602
+ successMessage: 'Authentication authorization URL retrieved',
603
+ metadata: {
604
+ authenticationMethodId: params.authenticationMethodId,
605
+ authenticationMethodName: authMethod.name,
606
+ scopeCount: params.scopes.length,
607
+ inputKeyCount: getObjectKeyCount(params.input)
608
+ },
609
+ onSuccess: result => ({
610
+ returnedInput: !!result.input,
611
+ hasCallbackState: !!result.callbackState
612
+ })
613
+ },
614
+ () =>
615
+ runWithContext(context, () =>
616
+ authMethod.getAuthorizationUrl({
617
+ redirectUri: params.redirectUri,
618
+ state: params.state,
619
+ input: params.input,
620
+ clientId: params.clientId,
621
+ clientSecret: params.clientSecret,
622
+ scopes: params.scopes,
623
+ config: getAuthConfig()
624
+ })
625
+ )
626
+ );
285
627
 
286
- return {
628
+ return withRequestTraces(context, {
287
629
  authorizationUrl: urlRes.url,
288
- input: urlRes.input
289
- };
630
+ input: urlRes.input,
631
+ callbackState: urlRes.callbackState
632
+ });
290
633
  }
291
634
 
292
635
  throw new ServiceError(
@@ -301,15 +644,47 @@ export let createProviderHandler = <ConfigType extends {}, AuthType extends {}>(
301
644
  let authMethod = getAuthMethod(slate, params.authenticationMethodId);
302
645
 
303
646
  if (authMethod.getProfile) {
304
- let profileRes = await authMethod.getProfile({
305
- output: params.output as any,
306
- input: params.input,
307
- scopes: params.scopes
308
- });
647
+ let context = getAuthContext();
648
+ let profileRes = await traceProviderCall(
649
+ {
650
+ component: 'auth',
651
+ functionName: 'getProfile',
652
+ message: 'Getting authentication profile',
653
+ successMessage: 'Authentication profile retrieved',
654
+ metadata: {
655
+ authenticationMethodId: params.authenticationMethodId,
656
+ authenticationMethodName: authMethod.name,
657
+ scopeCount: params.scopes.length,
658
+ inputKeyCount: getObjectKeyCount(params.input),
659
+ outputKeyCount: getObjectKeyCount(params.output)
660
+ },
661
+ onSuccess: result => ({
662
+ profileKeyCount: getObjectKeyCount(result.profile)
663
+ })
664
+ },
665
+ () =>
666
+ runWithContext(context, () => {
667
+ if (authMethod.type === 'auth.oauth') {
668
+ return authMethod.getProfile!({
669
+ output: params.output as any,
670
+ input: params.input,
671
+ scopes: params.scopes,
672
+ config: getAuthConfig()
673
+ });
674
+ }
675
+
676
+ return authMethod.getProfile!({
677
+ output: params.output as any,
678
+ input: params.input,
679
+ scopes: params.scopes,
680
+ config: getAuthConfig()
681
+ })!;
682
+ })
683
+ );
309
684
 
310
- return {
685
+ return withRequestTraces(context, {
311
686
  profile: profileRes.profile
312
- };
687
+ });
313
688
  }
314
689
 
315
690
  throw new ServiceError(
@@ -324,18 +699,53 @@ export let createProviderHandler = <ConfigType extends {}, AuthType extends {}>(
324
699
  let authMethod = getAuthMethod(slate, params.authenticationMethodId);
325
700
 
326
701
  if ('handleTokenRefresh' in authMethod && authMethod.handleTokenRefresh) {
327
- let refreshRes = await authMethod.handleTokenRefresh({
328
- output: params.output as any,
329
- input: params.input,
330
- clientId: params.clientId,
331
- clientSecret: params.clientSecret,
332
- scopes: params.scopes
333
- });
702
+ let context = getAuthContext();
703
+ let refreshRes = await traceProviderCall(
704
+ {
705
+ component: 'auth',
706
+ functionName: 'handleTokenRefresh',
707
+ message: 'Refreshing authentication token',
708
+ successMessage: 'Authentication token refreshed',
709
+ metadata: {
710
+ authenticationMethodId: params.authenticationMethodId,
711
+ authenticationMethodName: authMethod.name,
712
+ scopeCount: params.scopes.length,
713
+ inputKeyCount: getObjectKeyCount(params.input),
714
+ outputKeyCount: getObjectKeyCount(params.output)
715
+ },
716
+ onSuccess: result => ({
717
+ refreshedOutputKeyCount: getObjectKeyCount(result.output),
718
+ returnedInput: !!result.input
719
+ })
720
+ },
721
+ () =>
722
+ runWithContext(context, () => {
723
+ if (authMethod.type === 'auth.oauth') {
724
+ return authMethod.handleTokenRefresh!({
725
+ output: params.output as any,
726
+ input: params.input,
727
+ clientId: params.clientId,
728
+ clientSecret: params.clientSecret,
729
+ scopes: params.scopes,
730
+ config: getAuthConfig()
731
+ });
732
+ }
733
+
734
+ return authMethod.handleTokenRefresh!({
735
+ output: params.output as any,
736
+ input: params.input,
737
+ clientId: params.clientId,
738
+ clientSecret: params.clientSecret,
739
+ scopes: params.scopes,
740
+ config: getAuthConfig()
741
+ });
742
+ })
743
+ );
334
744
 
335
- return {
745
+ return withRequestTraces(context, {
336
746
  output: refreshRes.output,
337
747
  input: refreshRes.input
338
- };
748
+ });
339
749
  }
340
750
 
341
751
  throw new ServiceError(
@@ -348,8 +758,29 @@ export let createProviderHandler = <ConfigType extends {}, AuthType extends {}>(
348
758
  manager.onRequest('slates/actions.list', async ({ params }) => {
349
759
  getContextBasic();
350
760
 
761
+ let actions = params.includeAdapterActions
762
+ ? slate.actions
763
+ : slate.actions.filter(action => !action.adapter);
764
+
351
765
  return {
352
- actions: slate.actions.map(a => mapAction(slate, a))
766
+ actions: actions.map(a => mapAction(slate, a))
767
+ };
768
+ });
769
+
770
+ manager.onRequest('slates/adapters.list', async () => {
771
+ getContextBasic();
772
+
773
+ return {
774
+ adapters: slate.adapters.map(mapAdapter)
775
+ };
776
+ });
777
+
778
+ manager.onRequest('slates/adapter.get', async ({ params }) => {
779
+ getContextBasic();
780
+ let adapter = getAdapter(slate, params.adapterId);
781
+
782
+ return {
783
+ adapter: mapAdapter(adapter)
353
784
  };
354
785
  });
355
786
 
@@ -363,7 +794,6 @@ export let createProviderHandler = <ConfigType extends {}, AuthType extends {}>(
363
794
  });
364
795
 
365
796
  manager.onRequest('slates/action.tool.invoke', async ({ params }) => {
366
- let ctx = getContextFull();
367
797
  let action = getActionWithType(slate, 'tool', params.actionId);
368
798
 
369
799
  let input = validate(
@@ -373,11 +803,47 @@ export let createProviderHandler = <ConfigType extends {}, AuthType extends {}>(
373
803
  `Invalid input for tool ID: ${params.actionId}`
374
804
  );
375
805
 
376
- let res = await action.handleInvocation(
806
+ let invoke = async (context: SlatePublicContext<any>) => {
807
+ let res = await traceProviderCall(
808
+ {
809
+ component: 'action',
810
+ functionName: 'handleInvocation',
811
+ message: `Starting tool ${formatEntityLabel(action.name, action.key)}`,
812
+ successMessage: `Completed tool ${formatEntityLabel(action.name, action.key)}`,
813
+ errorMessage: `Tool ${formatEntityLabel(action.name, action.key)} failed`,
814
+ metadata: {
815
+ actionId: action.key,
816
+ actionName: action.name,
817
+ actionType: action.type,
818
+ isPublic: action.isPublic,
819
+ inputKeyCount: getObjectKeyCount(input)
820
+ },
821
+ onSuccess: result => ({
822
+ hasMessage: !!result.message,
823
+ actionResultMessage: result.message,
824
+ outputKeyCount: getObjectKeyCount(result.output),
825
+ attachmentCount: result.attachments?.length
826
+ })
827
+ },
828
+ () => runWithContext(context, () => action.handleInvocation(context as any))
829
+ );
830
+
831
+ return withRequestTraces(context, {
832
+ output: res.output,
833
+ message: res.message,
834
+ attachments: mergeAttachments(res.attachments, res.output)
835
+ });
836
+ };
837
+
838
+ if (action.isPublic) {
839
+ getContextBasic();
840
+ return invoke(new SlatePublicContext(input, slate.spec, logger));
841
+ }
842
+
843
+ let ctx = getContextFull();
844
+ return invoke(
377
845
  new SlateContext(ctx.config, input, ctx.auth?.output!, slate.spec, logger)
378
846
  );
379
-
380
- return { output: res.output, message: res.message };
381
847
  });
382
848
 
383
849
  manager.onRequest('slates/action.trigger.map_event', async ({ params }) => {
@@ -391,11 +857,31 @@ export let createProviderHandler = <ConfigType extends {}, AuthType extends {}>(
391
857
  `Invalid event for trigger ID: ${params.actionId}`
392
858
  );
393
859
 
394
- let res = await action.handleEvent(
395
- new SlateContext(ctx.config, input, ctx.auth?.output!, slate.spec, logger)
860
+ let context = new SlateContext(ctx.config, input, ctx.auth?.output!, slate.spec, logger);
861
+ let res = await traceProviderCall(
862
+ {
863
+ component: 'action',
864
+ functionName: 'handleEvent',
865
+ message: `Mapping event for trigger ${formatEntityLabel(action.name, action.key)}`,
866
+ successMessage: result =>
867
+ `Mapped trigger event "${result.type}" for ${formatEntityLabel(action.name, action.key)}`,
868
+ errorMessage: `Trigger ${formatEntityLabel(action.name, action.key)} failed while mapping an event`,
869
+ metadata: {
870
+ actionId: action.key,
871
+ actionName: action.name,
872
+ actionType: action.type,
873
+ inputKeyCount: getObjectKeyCount(input)
874
+ },
875
+ onSuccess: result => ({
876
+ eventType: result.type,
877
+ hasEventId: !!result.id,
878
+ outputKeyCount: getObjectKeyCount(result.output)
879
+ })
880
+ },
881
+ () => runWithContext(context, () => action.handleEvent(context))
396
882
  );
397
883
 
398
- return { id: res.id, type: res.type, output: res.output };
884
+ return withRequestTraces(context, { id: res.id, type: res.type, output: res.output });
399
885
  });
400
886
 
401
887
  manager.onRequest('slates/action.trigger.poll_events', async ({ params }) => {
@@ -410,17 +896,39 @@ export let createProviderHandler = <ConfigType extends {}, AuthType extends {}>(
410
896
  );
411
897
  }
412
898
 
413
- let res = await action.pollEvents(
414
- new SlateContext(
415
- ctx.config,
416
- { state: params.state },
417
- ctx.auth?.output!,
418
- slate.spec,
419
- logger
420
- )
899
+ let context = new SlateContext(
900
+ ctx.config,
901
+ { state: params.state },
902
+ ctx.auth?.output!,
903
+ slate.spec,
904
+ logger
905
+ );
906
+ let res = await traceProviderCall(
907
+ {
908
+ component: 'action',
909
+ functionName: 'pollEvents',
910
+ message: `Polling events for trigger ${formatEntityLabel(action.name, action.key)}`,
911
+ successMessage: result =>
912
+ `Polled ${result.inputs.length} event(s) for trigger ${formatEntityLabel(action.name, action.key)}`,
913
+ errorMessage: `Trigger ${formatEntityLabel(action.name, action.key)} failed while polling events`,
914
+ metadata: {
915
+ actionId: action.key,
916
+ actionName: action.name,
917
+ actionType: action.type,
918
+ hasPreviousState: params.state !== null
919
+ },
920
+ onSuccess: result => ({
921
+ inputCount: result.inputs.length,
922
+ hasUpdatedState: result.updatedState !== undefined
923
+ })
924
+ },
925
+ () => runWithContext(context, () => action.pollEvents!(context))
421
926
  );
422
927
 
423
- return { inputs: res.inputs, updatedState: res.updatedState };
928
+ return withRequestTraces(context, {
929
+ inputs: res.inputs,
930
+ updatedState: res.updatedState
931
+ });
424
932
  });
425
933
 
426
934
  manager.onRequest('slates/action.trigger.webhook_handle', async ({ params }) => {
@@ -443,17 +951,52 @@ export let createProviderHandler = <ConfigType extends {}, AuthType extends {}>(
443
951
  : null
444
952
  });
445
953
 
446
- let res = await action.handleRequest(
447
- new SlateContext(
448
- ctx.config,
449
- { request: req, state: params.state },
450
- ctx.auth?.output!,
451
- slate.spec,
452
- logger
453
- )
954
+ let context = new SlateContext(
955
+ ctx.config,
956
+ {
957
+ request: req,
958
+ state: params.state,
959
+ registrationDetails: params.registrationDetails ?? null
960
+ },
961
+ ctx.auth?.output!,
962
+ slate.spec,
963
+ logger
964
+ );
965
+ let res = await traceProviderCall(
966
+ {
967
+ component: 'action',
968
+ functionName: 'handleRequest',
969
+ message: `Handling webhook request for trigger ${formatEntityLabel(action.name, action.key)}`,
970
+ successMessage: result =>
971
+ `Received ${result.inputs.length} webhook event(s) for trigger ${formatEntityLabel(action.name, action.key)}`,
972
+ errorMessage: `Trigger ${formatEntityLabel(action.name, action.key)} failed while handling a webhook request`,
973
+ metadata: {
974
+ actionId: action.key,
975
+ actionName: action.name,
976
+ actionType: action.type,
977
+ requestMethod: params.method,
978
+ hasRequestBody: !!params.body,
979
+ hasPreviousState: params.state !== null
980
+ },
981
+ onSuccess: result => ({
982
+ inputCount: result.inputs.length,
983
+ hasUpdatedState: result.updatedState !== undefined,
984
+ hasResponse: result.response !== undefined
985
+ })
986
+ },
987
+ () => runWithContext(context, () => action.handleRequest!(context))
454
988
  );
455
989
 
456
- return { inputs: res.inputs, updatedState: res.updatedState };
990
+ let response =
991
+ res.response === undefined
992
+ ? undefined
993
+ : await serializeWebhookHttpResponse(res.response);
994
+
995
+ return withRequestTraces(context, {
996
+ inputs: res.inputs,
997
+ updatedState: res.updatedState,
998
+ response
999
+ });
457
1000
  });
458
1001
 
459
1002
  manager.onRequest('slates/action.trigger.webhook_register', async ({ params }) => {
@@ -468,17 +1011,37 @@ export let createProviderHandler = <ConfigType extends {}, AuthType extends {}>(
468
1011
  );
469
1012
  }
470
1013
 
471
- let res = await action.autoRegisterWebhook(
472
- new SlateContext(
473
- ctx.config,
474
- { webhookBaseUrl: params.webhookBaseUrl },
475
- ctx.auth?.output!,
476
- slate.spec,
477
- logger
478
- )
1014
+ let context = new SlateContext(
1015
+ ctx.config,
1016
+ { webhookBaseUrl: params.webhookBaseUrl },
1017
+ ctx.auth?.output!,
1018
+ slate.spec,
1019
+ logger
1020
+ );
1021
+ let res = await traceProviderCall(
1022
+ {
1023
+ component: 'action',
1024
+ functionName: 'autoRegisterWebhook',
1025
+ message: `Registering webhook for trigger ${formatEntityLabel(action.name, action.key)}`,
1026
+ successMessage: `Registered webhook for trigger ${formatEntityLabel(action.name, action.key)}`,
1027
+ errorMessage: `Trigger ${formatEntityLabel(action.name, action.key)} failed while registering a webhook`,
1028
+ metadata: {
1029
+ actionId: action.key,
1030
+ actionName: action.name,
1031
+ actionType: action.type
1032
+ },
1033
+ onSuccess: result => ({
1034
+ hasRegistrationDetails: result.registrationDetails !== undefined,
1035
+ hasState: result.state !== undefined
1036
+ })
1037
+ },
1038
+ () => runWithContext(context, () => action.autoRegisterWebhook!(context))
479
1039
  );
480
1040
 
481
- return { registrationDetails: res.registrationDetails, state: res.state };
1041
+ return withRequestTraces(context, {
1042
+ registrationDetails: res.registrationDetails,
1043
+ state: res.state
1044
+ });
482
1045
  });
483
1046
 
484
1047
  manager.onRequest('slates/action.trigger.webhook_unregister', async ({ params }) => {
@@ -493,20 +1056,35 @@ export let createProviderHandler = <ConfigType extends {}, AuthType extends {}>(
493
1056
  );
494
1057
  }
495
1058
 
496
- await action.autoUnregisterWebhook(
497
- new SlateContext(
498
- ctx.config,
499
- {
500
- webhookBaseUrl: params.webhookBaseUrl,
501
- registrationDetails: params.registrationDetails,
502
- state: params.state
503
- },
504
- ctx.auth?.output!,
505
- slate.spec,
506
- logger
507
- )
1059
+ let context = new SlateContext(
1060
+ ctx.config,
1061
+ {
1062
+ webhookBaseUrl: params.webhookBaseUrl,
1063
+ registrationDetails: params.registrationDetails,
1064
+ state: params.state
1065
+ },
1066
+ ctx.auth?.output!,
1067
+ slate.spec,
1068
+ logger
1069
+ );
1070
+ await traceProviderCall(
1071
+ {
1072
+ component: 'action',
1073
+ functionName: 'autoUnregisterWebhook',
1074
+ message: `Unregistering webhook for trigger ${formatEntityLabel(action.name, action.key)}`,
1075
+ successMessage: `Unregistered webhook for trigger ${formatEntityLabel(action.name, action.key)}`,
1076
+ errorMessage: `Trigger ${formatEntityLabel(action.name, action.key)} failed while unregistering a webhook`,
1077
+ metadata: {
1078
+ actionId: action.key,
1079
+ actionName: action.name,
1080
+ actionType: action.type,
1081
+ hasRegistrationDetails: params.registrationDetails !== null,
1082
+ hasPreviousState: params.state !== null
1083
+ }
1084
+ },
1085
+ () => runWithContext(context, () => action.autoUnregisterWebhook!(context))
508
1086
  );
509
1087
 
510
- return {};
1088
+ return withRequestTraces(context, {});
511
1089
  });
512
1090
  });