@slates/provider-handler 1.0.0-rc.31 → 1.0.0-rc.34
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 +46 -8
- package/dist/index.module.js +52 -9
- package/package.json +3 -3
- package/src/exposure.test.ts +161 -0
- package/src/index.ts +25 -9
- package/src/spec.test.ts +134 -0
- package/src/spec.ts +61 -5
package/dist/index.cjs
CHANGED
|
@@ -112,6 +112,7 @@ var mapAuthMethod = (slate, m) => ({
|
|
|
112
112
|
title: s.title,
|
|
113
113
|
description: s.description
|
|
114
114
|
})) : void 0,
|
|
115
|
+
adapters: "adapters" in m ? m.adapters : void 0,
|
|
115
116
|
inputSchema: toJsonSchema(m.inputSchema ?? import_zod.default.object({})),
|
|
116
117
|
outputSchema: toJsonSchema(slate.spec.auth.outputSchema),
|
|
117
118
|
capabilities: {
|
|
@@ -187,6 +188,24 @@ var mapAction = (_slate, a) => {
|
|
|
187
188
|
triggerGroupId: a.triggerGroup.key
|
|
188
189
|
};
|
|
189
190
|
};
|
|
191
|
+
var isMappableTrigger = (action) => action.type !== "trigger" || !!action.triggerGroup;
|
|
192
|
+
var getMappableAction = (slate, actionId) => {
|
|
193
|
+
let action = getAction(slate, actionId);
|
|
194
|
+
if (!isMappableTrigger(action)) {
|
|
195
|
+
throw new import_error2.ServiceError((0, import_error2.notFoundError)(`action`, actionId));
|
|
196
|
+
}
|
|
197
|
+
return action;
|
|
198
|
+
};
|
|
199
|
+
var getExposedActions = (slate, includeAdapterActions) => slate.actions.filter(
|
|
200
|
+
(action) => isMappableTrigger(action) && (includeAdapterActions || !action.adapter)
|
|
201
|
+
);
|
|
202
|
+
var getExposedAction = (slate, actionId, includeAdapterActions) => {
|
|
203
|
+
let action = getMappableAction(slate, actionId);
|
|
204
|
+
if (action.adapter && !includeAdapterActions) {
|
|
205
|
+
throw new import_error2.ServiceError((0, import_error2.notFoundError)(`action`, actionId));
|
|
206
|
+
}
|
|
207
|
+
return action;
|
|
208
|
+
};
|
|
190
209
|
var getTriggerGroup = (slate, triggerGroupId) => {
|
|
191
210
|
let group = slate.triggerGroups.find((g) => g.key === triggerGroupId);
|
|
192
211
|
if (!group) {
|
|
@@ -195,8 +214,16 @@ var getTriggerGroup = (slate, triggerGroupId) => {
|
|
|
195
214
|
return group;
|
|
196
215
|
};
|
|
197
216
|
var getTriggersForGroup = (slate, triggerGroupId) => slate.actions.filter(
|
|
198
|
-
(action) => action.type === "trigger" && action.triggerGroup
|
|
217
|
+
(action) => action.type === "trigger" && action.triggerGroup?.key === triggerGroupId
|
|
199
218
|
);
|
|
219
|
+
var getExposedTriggerGroups = (slate, includeAdapterActions) => {
|
|
220
|
+
let triggerGroupIds = new Set(
|
|
221
|
+
getExposedActions(slate, includeAdapterActions).flatMap(
|
|
222
|
+
(action) => action.type === "trigger" && action.triggerGroup ? [action.triggerGroup.key] : []
|
|
223
|
+
)
|
|
224
|
+
);
|
|
225
|
+
return slate.triggerGroups.filter((group) => triggerGroupIds.has(group.key));
|
|
226
|
+
};
|
|
200
227
|
var evaluateTriggerMatches = (slate, triggerGroupId, payload) => getTriggersForGroup(slate, triggerGroupId).filter((trigger) => trigger.matches(payload)).map((trigger) => trigger.key);
|
|
201
228
|
var getWebhookAutoRegistration = (group) => {
|
|
202
229
|
if (group.source !== "webhook" || !group.webhook?.autoRegistration) {
|
|
@@ -230,8 +257,12 @@ var mapTriggerGroup = (group) => ({
|
|
|
230
257
|
type: "webhook",
|
|
231
258
|
registration: group.webhook?.manualRegistration ? {
|
|
232
259
|
mode: "manual",
|
|
233
|
-
userConfigSchema: toJsonSchema(
|
|
234
|
-
|
|
260
|
+
userConfigSchema: toJsonSchema(
|
|
261
|
+
group.webhook.manualRegistration.userConfigSchema
|
|
262
|
+
),
|
|
263
|
+
fullConfigSchema: toJsonSchema(
|
|
264
|
+
group.webhook.manualRegistration.fullConfigSchema
|
|
265
|
+
)
|
|
235
266
|
} : { mode: "auto" }
|
|
236
267
|
}
|
|
237
268
|
});
|
|
@@ -415,6 +446,7 @@ var createProviderHandler = (slate, listeners) => (0, import_proto.createSlatesP
|
|
|
415
446
|
};
|
|
416
447
|
};
|
|
417
448
|
let supportsTriggerGroups = () => !!hubCapabilities.get()?.triggers;
|
|
449
|
+
let supportsAdapters = () => !!hubCapabilities.get()?.adapters;
|
|
418
450
|
let logger = new import_provider2.SlateLogger(listeners);
|
|
419
451
|
let providerTrace = {
|
|
420
452
|
providerId: slate.spec.key,
|
|
@@ -557,7 +589,7 @@ var createProviderHandler = (slate, listeners) => (0, import_proto.createSlatesP
|
|
|
557
589
|
liveInvocation: true
|
|
558
590
|
},
|
|
559
591
|
provider: {
|
|
560
|
-
triggerGroups:
|
|
592
|
+
triggerGroups: (slate.triggerGroups?.length ?? 0) > 0 && (slate.actions ?? []).every(isMappableTrigger)
|
|
561
593
|
}
|
|
562
594
|
}
|
|
563
595
|
}));
|
|
@@ -968,7 +1000,10 @@ var createProviderHandler = (slate, listeners) => (0, import_proto.createSlatesP
|
|
|
968
1000
|
});
|
|
969
1001
|
manager.onRequest("slates/actions.list", async ({ params }) => {
|
|
970
1002
|
getContextBasic();
|
|
971
|
-
let actions =
|
|
1003
|
+
let actions = getExposedActions(
|
|
1004
|
+
slate,
|
|
1005
|
+
!!params.includeAdapterActions && supportsAdapters()
|
|
1006
|
+
);
|
|
972
1007
|
if (!supportsTriggerGroups()) {
|
|
973
1008
|
actions = actions.filter((action) => action.type !== "trigger");
|
|
974
1009
|
}
|
|
@@ -979,11 +1014,14 @@ var createProviderHandler = (slate, listeners) => (0, import_proto.createSlatesP
|
|
|
979
1014
|
manager.onRequest("slates/adapters.list", async () => {
|
|
980
1015
|
getContextBasic();
|
|
981
1016
|
return {
|
|
982
|
-
adapters: slate.adapters.map(mapAdapter)
|
|
1017
|
+
adapters: supportsAdapters() ? slate.adapters.map(mapAdapter) : []
|
|
983
1018
|
};
|
|
984
1019
|
});
|
|
985
1020
|
manager.onRequest("slates/adapter.get", async ({ params }) => {
|
|
986
1021
|
getContextBasic();
|
|
1022
|
+
if (!supportsAdapters()) {
|
|
1023
|
+
throw new import_error4.ServiceError((0, import_error4.notFoundError)(`adapter`, params.adapterId));
|
|
1024
|
+
}
|
|
987
1025
|
let adapter = getAdapter(slate, params.adapterId);
|
|
988
1026
|
return {
|
|
989
1027
|
adapter: mapAdapter(adapter)
|
|
@@ -991,7 +1029,7 @@ var createProviderHandler = (slate, listeners) => (0, import_proto.createSlatesP
|
|
|
991
1029
|
});
|
|
992
1030
|
manager.onRequest("slates/action.get", async ({ params }) => {
|
|
993
1031
|
getContextBasic();
|
|
994
|
-
let action =
|
|
1032
|
+
let action = getExposedAction(slate, params.actionId, supportsAdapters());
|
|
995
1033
|
return {
|
|
996
1034
|
action: mapAction(slate, action)
|
|
997
1035
|
};
|
|
@@ -999,7 +1037,7 @@ var createProviderHandler = (slate, listeners) => (0, import_proto.createSlatesP
|
|
|
999
1037
|
manager.onRequest("slates/trigger_groups.list", async () => {
|
|
1000
1038
|
getContextBasic();
|
|
1001
1039
|
return {
|
|
1002
|
-
triggerGroups: slate.
|
|
1040
|
+
triggerGroups: getExposedTriggerGroups(slate, supportsAdapters()).map(mapTriggerGroup)
|
|
1003
1041
|
};
|
|
1004
1042
|
});
|
|
1005
1043
|
manager.onRequest("slates/trigger_group.get", async ({ params }) => {
|
package/dist/index.module.js
CHANGED
|
@@ -1,5 +1,10 @@
|
|
|
1
1
|
// src/index.ts
|
|
2
|
-
import {
|
|
2
|
+
import {
|
|
3
|
+
badRequestError as badRequestError3,
|
|
4
|
+
notFoundError as notFoundError2,
|
|
5
|
+
preconditionFailedError,
|
|
6
|
+
ServiceError as ServiceError4
|
|
7
|
+
} from "@lowerdeck/error";
|
|
3
8
|
import {
|
|
4
9
|
createSlatesProviderProtoHandler,
|
|
5
10
|
SLATES_PROTOCOL_VERSION
|
|
@@ -90,6 +95,7 @@ var mapAuthMethod = (slate, m) => ({
|
|
|
90
95
|
title: s.title,
|
|
91
96
|
description: s.description
|
|
92
97
|
})) : void 0,
|
|
98
|
+
adapters: "adapters" in m ? m.adapters : void 0,
|
|
93
99
|
inputSchema: toJsonSchema(m.inputSchema ?? z.object({})),
|
|
94
100
|
outputSchema: toJsonSchema(slate.spec.auth.outputSchema),
|
|
95
101
|
capabilities: {
|
|
@@ -165,6 +171,24 @@ var mapAction = (_slate, a) => {
|
|
|
165
171
|
triggerGroupId: a.triggerGroup.key
|
|
166
172
|
};
|
|
167
173
|
};
|
|
174
|
+
var isMappableTrigger = (action) => action.type !== "trigger" || !!action.triggerGroup;
|
|
175
|
+
var getMappableAction = (slate, actionId) => {
|
|
176
|
+
let action = getAction(slate, actionId);
|
|
177
|
+
if (!isMappableTrigger(action)) {
|
|
178
|
+
throw new ServiceError2(notFoundError(`action`, actionId));
|
|
179
|
+
}
|
|
180
|
+
return action;
|
|
181
|
+
};
|
|
182
|
+
var getExposedActions = (slate, includeAdapterActions) => slate.actions.filter(
|
|
183
|
+
(action) => isMappableTrigger(action) && (includeAdapterActions || !action.adapter)
|
|
184
|
+
);
|
|
185
|
+
var getExposedAction = (slate, actionId, includeAdapterActions) => {
|
|
186
|
+
let action = getMappableAction(slate, actionId);
|
|
187
|
+
if (action.adapter && !includeAdapterActions) {
|
|
188
|
+
throw new ServiceError2(notFoundError(`action`, actionId));
|
|
189
|
+
}
|
|
190
|
+
return action;
|
|
191
|
+
};
|
|
168
192
|
var getTriggerGroup = (slate, triggerGroupId) => {
|
|
169
193
|
let group = slate.triggerGroups.find((g) => g.key === triggerGroupId);
|
|
170
194
|
if (!group) {
|
|
@@ -173,8 +197,16 @@ var getTriggerGroup = (slate, triggerGroupId) => {
|
|
|
173
197
|
return group;
|
|
174
198
|
};
|
|
175
199
|
var getTriggersForGroup = (slate, triggerGroupId) => slate.actions.filter(
|
|
176
|
-
(action) => action.type === "trigger" && action.triggerGroup
|
|
200
|
+
(action) => action.type === "trigger" && action.triggerGroup?.key === triggerGroupId
|
|
177
201
|
);
|
|
202
|
+
var getExposedTriggerGroups = (slate, includeAdapterActions) => {
|
|
203
|
+
let triggerGroupIds = new Set(
|
|
204
|
+
getExposedActions(slate, includeAdapterActions).flatMap(
|
|
205
|
+
(action) => action.type === "trigger" && action.triggerGroup ? [action.triggerGroup.key] : []
|
|
206
|
+
)
|
|
207
|
+
);
|
|
208
|
+
return slate.triggerGroups.filter((group) => triggerGroupIds.has(group.key));
|
|
209
|
+
};
|
|
178
210
|
var evaluateTriggerMatches = (slate, triggerGroupId, payload) => getTriggersForGroup(slate, triggerGroupId).filter((trigger) => trigger.matches(payload)).map((trigger) => trigger.key);
|
|
179
211
|
var getWebhookAutoRegistration = (group) => {
|
|
180
212
|
if (group.source !== "webhook" || !group.webhook?.autoRegistration) {
|
|
@@ -208,8 +240,12 @@ var mapTriggerGroup = (group) => ({
|
|
|
208
240
|
type: "webhook",
|
|
209
241
|
registration: group.webhook?.manualRegistration ? {
|
|
210
242
|
mode: "manual",
|
|
211
|
-
userConfigSchema: toJsonSchema(
|
|
212
|
-
|
|
243
|
+
userConfigSchema: toJsonSchema(
|
|
244
|
+
group.webhook.manualRegistration.userConfigSchema
|
|
245
|
+
),
|
|
246
|
+
fullConfigSchema: toJsonSchema(
|
|
247
|
+
group.webhook.manualRegistration.fullConfigSchema
|
|
248
|
+
)
|
|
213
249
|
} : { mode: "auto" }
|
|
214
250
|
}
|
|
215
251
|
});
|
|
@@ -393,6 +429,7 @@ var createProviderHandler = (slate, listeners) => createSlatesProviderProtoHandl
|
|
|
393
429
|
};
|
|
394
430
|
};
|
|
395
431
|
let supportsTriggerGroups = () => !!hubCapabilities.get()?.triggers;
|
|
432
|
+
let supportsAdapters = () => !!hubCapabilities.get()?.adapters;
|
|
396
433
|
let logger = new SlateLogger(listeners);
|
|
397
434
|
let providerTrace = {
|
|
398
435
|
providerId: slate.spec.key,
|
|
@@ -535,7 +572,7 @@ var createProviderHandler = (slate, listeners) => createSlatesProviderProtoHandl
|
|
|
535
572
|
liveInvocation: true
|
|
536
573
|
},
|
|
537
574
|
provider: {
|
|
538
|
-
triggerGroups:
|
|
575
|
+
triggerGroups: (slate.triggerGroups?.length ?? 0) > 0 && (slate.actions ?? []).every(isMappableTrigger)
|
|
539
576
|
}
|
|
540
577
|
}
|
|
541
578
|
}));
|
|
@@ -946,7 +983,10 @@ var createProviderHandler = (slate, listeners) => createSlatesProviderProtoHandl
|
|
|
946
983
|
});
|
|
947
984
|
manager.onRequest("slates/actions.list", async ({ params }) => {
|
|
948
985
|
getContextBasic();
|
|
949
|
-
let actions =
|
|
986
|
+
let actions = getExposedActions(
|
|
987
|
+
slate,
|
|
988
|
+
!!params.includeAdapterActions && supportsAdapters()
|
|
989
|
+
);
|
|
950
990
|
if (!supportsTriggerGroups()) {
|
|
951
991
|
actions = actions.filter((action) => action.type !== "trigger");
|
|
952
992
|
}
|
|
@@ -957,11 +997,14 @@ var createProviderHandler = (slate, listeners) => createSlatesProviderProtoHandl
|
|
|
957
997
|
manager.onRequest("slates/adapters.list", async () => {
|
|
958
998
|
getContextBasic();
|
|
959
999
|
return {
|
|
960
|
-
adapters: slate.adapters.map(mapAdapter)
|
|
1000
|
+
adapters: supportsAdapters() ? slate.adapters.map(mapAdapter) : []
|
|
961
1001
|
};
|
|
962
1002
|
});
|
|
963
1003
|
manager.onRequest("slates/adapter.get", async ({ params }) => {
|
|
964
1004
|
getContextBasic();
|
|
1005
|
+
if (!supportsAdapters()) {
|
|
1006
|
+
throw new ServiceError4(notFoundError2(`adapter`, params.adapterId));
|
|
1007
|
+
}
|
|
965
1008
|
let adapter = getAdapter(slate, params.adapterId);
|
|
966
1009
|
return {
|
|
967
1010
|
adapter: mapAdapter(adapter)
|
|
@@ -969,7 +1012,7 @@ var createProviderHandler = (slate, listeners) => createSlatesProviderProtoHandl
|
|
|
969
1012
|
});
|
|
970
1013
|
manager.onRequest("slates/action.get", async ({ params }) => {
|
|
971
1014
|
getContextBasic();
|
|
972
|
-
let action =
|
|
1015
|
+
let action = getExposedAction(slate, params.actionId, supportsAdapters());
|
|
973
1016
|
return {
|
|
974
1017
|
action: mapAction(slate, action)
|
|
975
1018
|
};
|
|
@@ -977,7 +1020,7 @@ var createProviderHandler = (slate, listeners) => createSlatesProviderProtoHandl
|
|
|
977
1020
|
manager.onRequest("slates/trigger_groups.list", async () => {
|
|
978
1021
|
getContextBasic();
|
|
979
1022
|
return {
|
|
980
|
-
triggerGroups: slate.
|
|
1023
|
+
triggerGroups: getExposedTriggerGroups(slate, supportsAdapters()).map(mapTriggerGroup)
|
|
981
1024
|
};
|
|
982
1025
|
});
|
|
983
1026
|
manager.onRequest("slates/trigger_group.get", async ({ params }) => {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@slates/provider-handler",
|
|
3
|
-
"version": "1.0.0-rc.
|
|
3
|
+
"version": "1.0.0-rc.34",
|
|
4
4
|
"publishConfig": {
|
|
5
5
|
"access": "public"
|
|
6
6
|
},
|
|
@@ -32,8 +32,8 @@
|
|
|
32
32
|
},
|
|
33
33
|
"dependencies": {
|
|
34
34
|
"@lowerdeck/error": "^1.1.0",
|
|
35
|
-
"@slates/proto": "1.0.0-rc.
|
|
36
|
-
"@slates/provider": "1.0.0-rc.
|
|
35
|
+
"@slates/proto": "1.0.0-rc.20",
|
|
36
|
+
"@slates/provider": "1.0.0-rc.24",
|
|
37
37
|
"p-queue": "^6.6.2",
|
|
38
38
|
"zod": "^4.2.1"
|
|
39
39
|
},
|
|
@@ -0,0 +1,161 @@
|
|
|
1
|
+
import { SLATES_PROTOCOL_VERSION, SlatesProviderProtoHandlerManager } from '@slates/proto';
|
|
2
|
+
import { describe, expect, it } from 'vitest';
|
|
3
|
+
import { z } from 'zod';
|
|
4
|
+
import { createProviderHandler } from './index';
|
|
5
|
+
|
|
6
|
+
let createManager = async () => {
|
|
7
|
+
let providerGroup = {
|
|
8
|
+
key: 'provider_group',
|
|
9
|
+
name: 'Provider Group',
|
|
10
|
+
description: undefined,
|
|
11
|
+
metadata: {},
|
|
12
|
+
source: 'polling',
|
|
13
|
+
polling: { intervalSeconds: 60 }
|
|
14
|
+
};
|
|
15
|
+
let adapterGroup = {
|
|
16
|
+
key: 'adapter_group',
|
|
17
|
+
name: 'Adapter Group',
|
|
18
|
+
description: undefined,
|
|
19
|
+
metadata: {},
|
|
20
|
+
source: 'polling',
|
|
21
|
+
polling: { intervalSeconds: 60 }
|
|
22
|
+
};
|
|
23
|
+
let emptyGroup = {
|
|
24
|
+
key: 'empty_group',
|
|
25
|
+
name: 'Empty Group',
|
|
26
|
+
description: undefined,
|
|
27
|
+
metadata: {},
|
|
28
|
+
source: 'polling',
|
|
29
|
+
polling: { intervalSeconds: 60 }
|
|
30
|
+
};
|
|
31
|
+
let action = {
|
|
32
|
+
name: 'Action',
|
|
33
|
+
description: undefined,
|
|
34
|
+
instructions: undefined,
|
|
35
|
+
constraints: undefined,
|
|
36
|
+
tags: [],
|
|
37
|
+
metadata: {},
|
|
38
|
+
scopes: [],
|
|
39
|
+
authMethods: [],
|
|
40
|
+
docs: [],
|
|
41
|
+
inputSchema: z.object({}),
|
|
42
|
+
outputSchema: z.object({}),
|
|
43
|
+
matches: () => true,
|
|
44
|
+
map: async () => ({ type: 'event', output: {} })
|
|
45
|
+
};
|
|
46
|
+
let slate = {
|
|
47
|
+
spec: { key: 'test', name: 'Test' },
|
|
48
|
+
adapters: [{ id: 'chat', name: 'Chat', capabilities: [] }],
|
|
49
|
+
actions: [
|
|
50
|
+
{ ...action, key: 'provider_tool', type: 'tool', isPublic: false },
|
|
51
|
+
{
|
|
52
|
+
...action,
|
|
53
|
+
key: 'adapter_tool',
|
|
54
|
+
type: 'tool',
|
|
55
|
+
adapter: 'chat',
|
|
56
|
+
isPublic: false
|
|
57
|
+
},
|
|
58
|
+
{ ...action, key: 'provider_trigger', type: 'trigger', triggerGroup: providerGroup },
|
|
59
|
+
{
|
|
60
|
+
...action,
|
|
61
|
+
key: 'adapter_trigger',
|
|
62
|
+
type: 'trigger',
|
|
63
|
+
adapter: 'chat',
|
|
64
|
+
triggerGroup: adapterGroup
|
|
65
|
+
}
|
|
66
|
+
],
|
|
67
|
+
triggerGroups: [providerGroup, adapterGroup, emptyGroup]
|
|
68
|
+
};
|
|
69
|
+
let manager = await createProviderHandler(slate as any, []).run();
|
|
70
|
+
|
|
71
|
+
await SlatesProviderProtoHandlerManager.handleInput(manager, {
|
|
72
|
+
jsonrpc: '2.0',
|
|
73
|
+
method: 'slates/hello',
|
|
74
|
+
params: { protocol: SLATES_PROTOCOL_VERSION }
|
|
75
|
+
});
|
|
76
|
+
await SlatesProviderProtoHandlerManager.handleInput(manager, {
|
|
77
|
+
jsonrpc: '2.0',
|
|
78
|
+
method: 'slates/participant.set',
|
|
79
|
+
params: { participants: [] }
|
|
80
|
+
});
|
|
81
|
+
|
|
82
|
+
return manager;
|
|
83
|
+
};
|
|
84
|
+
|
|
85
|
+
let request = async (
|
|
86
|
+
manager: SlatesProviderProtoHandlerManager,
|
|
87
|
+
method:
|
|
88
|
+
| 'slates/actions.list'
|
|
89
|
+
| 'slates/action.get'
|
|
90
|
+
| 'slates/adapters.list'
|
|
91
|
+
| 'slates/adapter.get'
|
|
92
|
+
| 'slates/trigger_groups.list',
|
|
93
|
+
params: Record<string, unknown> = {}
|
|
94
|
+
) =>
|
|
95
|
+
SlatesProviderProtoHandlerManager.handleInput(manager, {
|
|
96
|
+
jsonrpc: '2.0',
|
|
97
|
+
id: method,
|
|
98
|
+
method,
|
|
99
|
+
params
|
|
100
|
+
} as any);
|
|
101
|
+
|
|
102
|
+
describe('adapter capability exposure', () => {
|
|
103
|
+
it('hides adapter surfaces from a legacy hub that only supports triggers', async () => {
|
|
104
|
+
let manager = await createManager();
|
|
105
|
+
|
|
106
|
+
await SlatesProviderProtoHandlerManager.handleInput(manager, {
|
|
107
|
+
jsonrpc: '2.0',
|
|
108
|
+
method: 'slates/hub.capabilities.set',
|
|
109
|
+
params: { capabilities: { triggers: true } }
|
|
110
|
+
});
|
|
111
|
+
|
|
112
|
+
expect(await request(manager, 'slates/adapters.list')).toMatchObject({
|
|
113
|
+
result: { adapters: [] }
|
|
114
|
+
});
|
|
115
|
+
expect(
|
|
116
|
+
await request(manager, 'slates/actions.list', { includeAdapterActions: true })
|
|
117
|
+
).toMatchObject({
|
|
118
|
+
result: { actions: [{ id: 'provider_tool' }, { id: 'provider_trigger' }] }
|
|
119
|
+
});
|
|
120
|
+
expect(await request(manager, 'slates/trigger_groups.list')).toMatchObject({
|
|
121
|
+
result: { triggerGroups: [{ id: 'provider_group' }] }
|
|
122
|
+
});
|
|
123
|
+
expect(await request(manager, 'slates/adapter.get', { adapterId: 'chat' })).toHaveProperty(
|
|
124
|
+
'error'
|
|
125
|
+
);
|
|
126
|
+
expect(
|
|
127
|
+
await request(manager, 'slates/action.get', { actionId: 'adapter_tool' })
|
|
128
|
+
).toHaveProperty('error');
|
|
129
|
+
});
|
|
130
|
+
|
|
131
|
+
it('exposes adapters and their actions when the hub enables them', async () => {
|
|
132
|
+
let manager = await createManager();
|
|
133
|
+
|
|
134
|
+
await SlatesProviderProtoHandlerManager.handleInput(manager, {
|
|
135
|
+
jsonrpc: '2.0',
|
|
136
|
+
method: 'slates/hub.capabilities.set',
|
|
137
|
+
params: { capabilities: { adapters: true, triggers: true } }
|
|
138
|
+
});
|
|
139
|
+
|
|
140
|
+
expect(await request(manager, 'slates/adapters.list')).toMatchObject({
|
|
141
|
+
result: { adapters: [{ id: 'chat' }] }
|
|
142
|
+
});
|
|
143
|
+
expect(
|
|
144
|
+
await request(manager, 'slates/actions.list', { includeAdapterActions: true })
|
|
145
|
+
).toMatchObject({
|
|
146
|
+
result: {
|
|
147
|
+
actions: [
|
|
148
|
+
{ id: 'provider_tool' },
|
|
149
|
+
{ id: 'adapter_tool' },
|
|
150
|
+
{ id: 'provider_trigger' },
|
|
151
|
+
{ id: 'adapter_trigger' }
|
|
152
|
+
]
|
|
153
|
+
}
|
|
154
|
+
});
|
|
155
|
+
expect(await request(manager, 'slates/trigger_groups.list')).toMatchObject({
|
|
156
|
+
result: {
|
|
157
|
+
triggerGroups: [{ id: 'provider_group' }, { id: 'adapter_group' }]
|
|
158
|
+
}
|
|
159
|
+
});
|
|
160
|
+
});
|
|
161
|
+
});
|
package/src/index.ts
CHANGED
|
@@ -1,4 +1,9 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import {
|
|
2
|
+
badRequestError,
|
|
3
|
+
notFoundError,
|
|
4
|
+
preconditionFailedError,
|
|
5
|
+
ServiceError
|
|
6
|
+
} from '@lowerdeck/error';
|
|
2
7
|
import {
|
|
3
8
|
createSlatesProviderProtoHandler,
|
|
4
9
|
SLATES_PROTOCOL_VERSION,
|
|
@@ -19,13 +24,16 @@ import {
|
|
|
19
24
|
import { PQueue } from './pQueue';
|
|
20
25
|
import {
|
|
21
26
|
evaluateTriggerMatches,
|
|
22
|
-
getAction,
|
|
23
27
|
getActionWithType,
|
|
24
28
|
getAdapter,
|
|
25
29
|
getAuthMethod,
|
|
30
|
+
getExposedAction,
|
|
31
|
+
getExposedActions,
|
|
32
|
+
getExposedTriggerGroups,
|
|
26
33
|
getTriggerGroup,
|
|
27
34
|
getWebhookAutoRegistration,
|
|
28
35
|
getWebhookManualRegistration,
|
|
36
|
+
isMappableTrigger,
|
|
29
37
|
mapAction,
|
|
30
38
|
mapAdapter,
|
|
31
39
|
mapAuthMethod,
|
|
@@ -190,6 +198,7 @@ export let createProviderHandler = <ConfigType extends {}, AuthType extends {}>(
|
|
|
190
198
|
|
|
191
199
|
let hubCapabilities = new State<{
|
|
192
200
|
attachments?: { directUpload?: { enabled: boolean; maxAttachmentSizeBytes?: number } };
|
|
201
|
+
adapters?: boolean;
|
|
193
202
|
triggers?: boolean;
|
|
194
203
|
} | null>(null);
|
|
195
204
|
let liveInvocation = new State<Pick<SlateLiveInvocationInfo, 'token' | 'baseUrl'> | null>(
|
|
@@ -210,6 +219,7 @@ export let createProviderHandler = <ConfigType extends {}, AuthType extends {}>(
|
|
|
210
219
|
};
|
|
211
220
|
|
|
212
221
|
let supportsTriggerGroups = () => !!hubCapabilities.get()?.triggers;
|
|
222
|
+
let supportsAdapters = () => !!hubCapabilities.get()?.adapters;
|
|
213
223
|
|
|
214
224
|
let logger = new SlateLogger(listeners);
|
|
215
225
|
let providerTrace = {
|
|
@@ -402,7 +412,9 @@ export let createProviderHandler = <ConfigType extends {}, AuthType extends {}>(
|
|
|
402
412
|
liveInvocation: true
|
|
403
413
|
},
|
|
404
414
|
provider: {
|
|
405
|
-
triggerGroups:
|
|
415
|
+
triggerGroups:
|
|
416
|
+
(slate.triggerGroups?.length ?? 0) > 0 &&
|
|
417
|
+
(slate.actions ?? []).every(isMappableTrigger)
|
|
406
418
|
}
|
|
407
419
|
}
|
|
408
420
|
}));
|
|
@@ -864,9 +876,10 @@ export let createProviderHandler = <ConfigType extends {}, AuthType extends {}>(
|
|
|
864
876
|
manager.onRequest('slates/actions.list', async ({ params }) => {
|
|
865
877
|
getContextBasic();
|
|
866
878
|
|
|
867
|
-
let actions =
|
|
868
|
-
|
|
869
|
-
|
|
879
|
+
let actions = getExposedActions(
|
|
880
|
+
slate,
|
|
881
|
+
!!params.includeAdapterActions && supportsAdapters()
|
|
882
|
+
);
|
|
870
883
|
|
|
871
884
|
if (!supportsTriggerGroups()) {
|
|
872
885
|
actions = actions.filter(action => action.type !== 'trigger');
|
|
@@ -881,12 +894,15 @@ export let createProviderHandler = <ConfigType extends {}, AuthType extends {}>(
|
|
|
881
894
|
getContextBasic();
|
|
882
895
|
|
|
883
896
|
return {
|
|
884
|
-
adapters: slate.adapters.map(mapAdapter)
|
|
897
|
+
adapters: supportsAdapters() ? slate.adapters.map(mapAdapter) : []
|
|
885
898
|
};
|
|
886
899
|
});
|
|
887
900
|
|
|
888
901
|
manager.onRequest('slates/adapter.get', async ({ params }) => {
|
|
889
902
|
getContextBasic();
|
|
903
|
+
if (!supportsAdapters()) {
|
|
904
|
+
throw new ServiceError(notFoundError(`adapter`, params.adapterId));
|
|
905
|
+
}
|
|
890
906
|
let adapter = getAdapter(slate, params.adapterId);
|
|
891
907
|
|
|
892
908
|
return {
|
|
@@ -896,7 +912,7 @@ export let createProviderHandler = <ConfigType extends {}, AuthType extends {}>(
|
|
|
896
912
|
|
|
897
913
|
manager.onRequest('slates/action.get', async ({ params }) => {
|
|
898
914
|
getContextBasic();
|
|
899
|
-
let action =
|
|
915
|
+
let action = getExposedAction(slate, params.actionId, supportsAdapters());
|
|
900
916
|
|
|
901
917
|
return {
|
|
902
918
|
action: mapAction(slate, action)
|
|
@@ -907,7 +923,7 @@ export let createProviderHandler = <ConfigType extends {}, AuthType extends {}>(
|
|
|
907
923
|
getContextBasic();
|
|
908
924
|
|
|
909
925
|
return {
|
|
910
|
-
triggerGroups: slate.
|
|
926
|
+
triggerGroups: getExposedTriggerGroups(slate, supportsAdapters()).map(mapTriggerGroup)
|
|
911
927
|
};
|
|
912
928
|
});
|
|
913
929
|
|
package/src/spec.test.ts
ADDED
|
@@ -0,0 +1,134 @@
|
|
|
1
|
+
import { isServiceError } from '@lowerdeck/error';
|
|
2
|
+
import { describe, expect, it } from 'vitest';
|
|
3
|
+
import { z } from 'zod';
|
|
4
|
+
import {
|
|
5
|
+
getExposedAction,
|
|
6
|
+
getExposedActions,
|
|
7
|
+
getExposedTriggerGroups,
|
|
8
|
+
getMappableAction,
|
|
9
|
+
getTriggersForGroup,
|
|
10
|
+
isMappableTrigger,
|
|
11
|
+
mapAction
|
|
12
|
+
} from './spec';
|
|
13
|
+
|
|
14
|
+
let baseAction = {
|
|
15
|
+
key: 'do_thing',
|
|
16
|
+
name: 'Do Thing',
|
|
17
|
+
description: 'desc',
|
|
18
|
+
instructions: undefined,
|
|
19
|
+
constraints: undefined,
|
|
20
|
+
tags: [],
|
|
21
|
+
metadata: {},
|
|
22
|
+
scopes: [],
|
|
23
|
+
authMethods: [],
|
|
24
|
+
docs: [],
|
|
25
|
+
adapter: undefined,
|
|
26
|
+
inputSchema: z.object({}),
|
|
27
|
+
outputSchema: z.object({})
|
|
28
|
+
};
|
|
29
|
+
|
|
30
|
+
let toolAction: any = { ...baseAction, type: 'tool', isPublic: false };
|
|
31
|
+
|
|
32
|
+
let validTriggerGroup = { key: 'my_group', name: 'My Group' };
|
|
33
|
+
let validTrigger: any = {
|
|
34
|
+
...baseAction,
|
|
35
|
+
type: 'trigger',
|
|
36
|
+
triggerGroup: validTriggerGroup,
|
|
37
|
+
matches: () => true,
|
|
38
|
+
map: () => ({})
|
|
39
|
+
};
|
|
40
|
+
|
|
41
|
+
let incompatibleTrigger: any = { ...baseAction, type: 'trigger', triggerGroup: undefined };
|
|
42
|
+
|
|
43
|
+
describe('isMappableTrigger', () => {
|
|
44
|
+
it('is true for tool actions', () => {
|
|
45
|
+
expect(isMappableTrigger(toolAction)).toBe(true);
|
|
46
|
+
});
|
|
47
|
+
|
|
48
|
+
it('is true for triggers with a trigger group', () => {
|
|
49
|
+
expect(isMappableTrigger(validTrigger)).toBe(true);
|
|
50
|
+
});
|
|
51
|
+
|
|
52
|
+
it('is false for triggers missing a trigger group', () => {
|
|
53
|
+
expect(isMappableTrigger(incompatibleTrigger)).toBe(false);
|
|
54
|
+
});
|
|
55
|
+
});
|
|
56
|
+
|
|
57
|
+
describe('mapAction', () => {
|
|
58
|
+
it('maps a well-formed trigger', () => {
|
|
59
|
+
let mapped = mapAction({} as any, validTrigger);
|
|
60
|
+
expect(mapped).toMatchObject({ type: 'action.trigger', triggerGroupId: 'my_group' });
|
|
61
|
+
});
|
|
62
|
+
});
|
|
63
|
+
|
|
64
|
+
describe('getTriggersForGroup', () => {
|
|
65
|
+
it('filters out triggers missing a trigger group instead of throwing', () => {
|
|
66
|
+
let slate = { actions: [toolAction, validTrigger, incompatibleTrigger] } as any;
|
|
67
|
+
|
|
68
|
+
expect(getTriggersForGroup(slate, 'my_group')).toEqual([validTrigger]);
|
|
69
|
+
expect(getTriggersForGroup(slate, 'other_group')).toEqual([]);
|
|
70
|
+
});
|
|
71
|
+
});
|
|
72
|
+
|
|
73
|
+
describe('getMappableAction', () => {
|
|
74
|
+
it('returns tool and well-formed trigger actions', () => {
|
|
75
|
+
let slate = { actions: [toolAction, validTrigger] } as any;
|
|
76
|
+
|
|
77
|
+
expect(getMappableAction(slate, 'do_thing')).toBe(toolAction);
|
|
78
|
+
});
|
|
79
|
+
|
|
80
|
+
it('treats a trigger missing its group as not found instead of crashing', () => {
|
|
81
|
+
let slate = { actions: [incompatibleTrigger] } as any;
|
|
82
|
+
|
|
83
|
+
try {
|
|
84
|
+
getMappableAction(slate, 'do_thing');
|
|
85
|
+
expect.unreachable();
|
|
86
|
+
} catch (e) {
|
|
87
|
+
expect(isServiceError(e)).toBe(true);
|
|
88
|
+
expect((e as any).data.status).toBe(404);
|
|
89
|
+
}
|
|
90
|
+
});
|
|
91
|
+
});
|
|
92
|
+
|
|
93
|
+
describe('adapter exposure', () => {
|
|
94
|
+
let providerTool: any = { ...toolAction, key: 'provider_tool' };
|
|
95
|
+
let adapterTool: any = { ...toolAction, key: 'adapter_tool', adapter: 'chat' };
|
|
96
|
+
let providerGroup = { key: 'provider_group', name: 'Provider Group' };
|
|
97
|
+
let adapterGroup = { key: 'adapter_group', name: 'Adapter Group' };
|
|
98
|
+
let emptyGroup = { key: 'empty_group', name: 'Empty Group' };
|
|
99
|
+
let providerTrigger: any = {
|
|
100
|
+
...validTrigger,
|
|
101
|
+
key: 'provider_trigger',
|
|
102
|
+
triggerGroup: providerGroup
|
|
103
|
+
};
|
|
104
|
+
let adapterTrigger: any = {
|
|
105
|
+
...validTrigger,
|
|
106
|
+
key: 'adapter_trigger',
|
|
107
|
+
adapter: 'chat',
|
|
108
|
+
triggerGroup: adapterGroup
|
|
109
|
+
};
|
|
110
|
+
let slate = {
|
|
111
|
+
actions: [providerTool, adapterTool, providerTrigger, adapterTrigger],
|
|
112
|
+
triggerGroups: [providerGroup, adapterGroup, emptyGroup]
|
|
113
|
+
} as any;
|
|
114
|
+
|
|
115
|
+
it('only exposes adapter actions when adapters are enabled', () => {
|
|
116
|
+
expect(getExposedActions(slate, false)).toEqual([providerTool, providerTrigger]);
|
|
117
|
+
expect(getExposedActions(slate, true)).toEqual([
|
|
118
|
+
providerTool,
|
|
119
|
+
adapterTool,
|
|
120
|
+
providerTrigger,
|
|
121
|
+
adapterTrigger
|
|
122
|
+
]);
|
|
123
|
+
});
|
|
124
|
+
|
|
125
|
+
it('does not expose an adapter action directly when adapters are disabled', () => {
|
|
126
|
+
expect(() => getExposedAction(slate, 'adapter_tool', false)).toThrow();
|
|
127
|
+
expect(getExposedAction(slate, 'adapter_tool', true)).toBe(adapterTool);
|
|
128
|
+
});
|
|
129
|
+
|
|
130
|
+
it('only exposes trigger groups that contain an exposed trigger', () => {
|
|
131
|
+
expect(getExposedTriggerGroups(slate, false)).toEqual([providerGroup]);
|
|
132
|
+
expect(getExposedTriggerGroups(slate, true)).toEqual([providerGroup, adapterGroup]);
|
|
133
|
+
});
|
|
134
|
+
});
|
package/src/spec.ts
CHANGED
|
@@ -8,8 +8,8 @@ import type {
|
|
|
8
8
|
import {
|
|
9
9
|
type Slate,
|
|
10
10
|
type SlateAdapter,
|
|
11
|
-
|
|
12
|
-
|
|
11
|
+
SlateDefaultPollingIntervalSeconds,
|
|
12
|
+
type SlateTrigger
|
|
13
13
|
} from '@slates/provider';
|
|
14
14
|
import z from 'zod';
|
|
15
15
|
import { toJsonSchema } from './validation';
|
|
@@ -47,6 +47,8 @@ export let mapAuthMethod = <ConfigType extends {}, AuthType extends {}>(
|
|
|
47
47
|
}))
|
|
48
48
|
: undefined,
|
|
49
49
|
|
|
50
|
+
adapters: 'adapters' in m ? m.adapters : undefined,
|
|
51
|
+
|
|
50
52
|
inputSchema: toJsonSchema(m.inputSchema ?? z.object({})),
|
|
51
53
|
outputSchema: toJsonSchema(slate.spec.auth.outputSchema),
|
|
52
54
|
|
|
@@ -155,6 +157,43 @@ export let mapAction = <ConfigType extends {}, AuthType extends {}>(
|
|
|
155
157
|
};
|
|
156
158
|
};
|
|
157
159
|
|
|
160
|
+
export let isMappableTrigger = <ConfigType extends {}, AuthType extends {}>(
|
|
161
|
+
action: Slate<ConfigType, AuthType>['actions'][number]
|
|
162
|
+
): boolean => action.type !== 'trigger' || !!action.triggerGroup;
|
|
163
|
+
|
|
164
|
+
export let getMappableAction = <ConfigType extends {}, AuthType extends {}>(
|
|
165
|
+
slate: Slate<ConfigType, AuthType>,
|
|
166
|
+
actionId: string
|
|
167
|
+
) => {
|
|
168
|
+
let action = getAction(slate, actionId);
|
|
169
|
+
if (!isMappableTrigger(action)) {
|
|
170
|
+
throw new ServiceError(notFoundError(`action`, actionId));
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
return action;
|
|
174
|
+
};
|
|
175
|
+
|
|
176
|
+
export let getExposedActions = <ConfigType extends {}, AuthType extends {}>(
|
|
177
|
+
slate: Slate<ConfigType, AuthType>,
|
|
178
|
+
includeAdapterActions: boolean
|
|
179
|
+
) =>
|
|
180
|
+
slate.actions.filter(
|
|
181
|
+
action => isMappableTrigger(action) && (includeAdapterActions || !action.adapter)
|
|
182
|
+
);
|
|
183
|
+
|
|
184
|
+
export let getExposedAction = <ConfigType extends {}, AuthType extends {}>(
|
|
185
|
+
slate: Slate<ConfigType, AuthType>,
|
|
186
|
+
actionId: string,
|
|
187
|
+
includeAdapterActions: boolean
|
|
188
|
+
) => {
|
|
189
|
+
let action = getMappableAction(slate, actionId);
|
|
190
|
+
if (action.adapter && !includeAdapterActions) {
|
|
191
|
+
throw new ServiceError(notFoundError(`action`, actionId));
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
return action;
|
|
195
|
+
};
|
|
196
|
+
|
|
158
197
|
export let getTriggerGroup = <ConfigType extends {}, AuthType extends {}>(
|
|
159
198
|
slate: Slate<ConfigType, AuthType>,
|
|
160
199
|
triggerGroupId: string
|
|
@@ -173,9 +212,22 @@ export let getTriggersForGroup = <ConfigType extends {}, AuthType extends {}>(
|
|
|
173
212
|
): SlateTrigger<ConfigType, AuthType, any, any>[] =>
|
|
174
213
|
slate.actions.filter(
|
|
175
214
|
(action): action is SlateTrigger<ConfigType, AuthType, any, any> =>
|
|
176
|
-
action.type === 'trigger' && action.triggerGroup
|
|
215
|
+
action.type === 'trigger' && action.triggerGroup?.key === triggerGroupId
|
|
177
216
|
);
|
|
178
217
|
|
|
218
|
+
export let getExposedTriggerGroups = <ConfigType extends {}, AuthType extends {}>(
|
|
219
|
+
slate: Slate<ConfigType, AuthType>,
|
|
220
|
+
includeAdapterActions: boolean
|
|
221
|
+
) => {
|
|
222
|
+
let triggerGroupIds = new Set(
|
|
223
|
+
getExposedActions(slate, includeAdapterActions).flatMap(action =>
|
|
224
|
+
action.type === 'trigger' && action.triggerGroup ? [action.triggerGroup.key] : []
|
|
225
|
+
)
|
|
226
|
+
);
|
|
227
|
+
|
|
228
|
+
return slate.triggerGroups.filter(group => triggerGroupIds.has(group.key));
|
|
229
|
+
};
|
|
230
|
+
|
|
179
231
|
export let evaluateTriggerMatches = <ConfigType extends {}, AuthType extends {}>(
|
|
180
232
|
slate: Slate<ConfigType, AuthType>,
|
|
181
233
|
triggerGroupId: string,
|
|
@@ -231,8 +283,12 @@ export let mapTriggerGroup = <ConfigType extends {}, AuthType extends {}>(
|
|
|
231
283
|
registration: group.webhook?.manualRegistration
|
|
232
284
|
? {
|
|
233
285
|
mode: 'manual',
|
|
234
|
-
userConfigSchema: toJsonSchema(
|
|
235
|
-
|
|
286
|
+
userConfigSchema: toJsonSchema(
|
|
287
|
+
group.webhook.manualRegistration.userConfigSchema
|
|
288
|
+
),
|
|
289
|
+
fullConfigSchema: toJsonSchema(
|
|
290
|
+
group.webhook.manualRegistration.fullConfigSchema
|
|
291
|
+
)
|
|
236
292
|
}
|
|
237
293
|
: { mode: 'auto' }
|
|
238
294
|
}
|