@slates/provider-handler 1.0.0-rc.4 → 1.0.0-rc.41
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 +1702 -2
- package/dist/index.d.cts +8 -0
- package/dist/index.d.ts +6 -3
- package/dist/index.module.js +1685 -2
- package/package.json +12 -10
- package/src/adminSetup.test.ts +189 -0
- package/src/attachments.test.ts +171 -0
- package/src/exposure.test.ts +216 -0
- package/src/index.ts +1301 -134
- package/src/pQueue.test.ts +18 -0
- package/src/pQueue.ts +18 -0
- package/src/spec.test.ts +232 -0
- package/src/spec.ts +219 -19
- package/src/validation.ts +1 -1
- package/src/webhook.test.ts +65 -0
- package/src/webhook.ts +69 -0
- package/src/webhookProcess.test.ts +99 -0
- package/dist/action/action.d.ts +0 -90
- package/dist/action/action.d.ts.map +0 -1
- package/dist/action/builder.d.ts +0 -27
- package/dist/action/builder.d.ts.map +0 -1
- package/dist/action/index.d.ts +0 -5
- package/dist/action/index.d.ts.map +0 -1
- package/dist/action/tool.d.ts +0 -11
- package/dist/action/tool.d.ts.map +0 -1
- package/dist/action/trigger.d.ts +0 -14
- package/dist/action/trigger.d.ts.map +0 -1
- package/dist/auth/auth.d.ts +0 -26
- package/dist/auth/auth.d.ts.map +0 -1
- package/dist/auth/index.d.ts +0 -3
- package/dist/auth/index.d.ts.map +0 -1
- package/dist/auth/types.d.ts +0 -148
- package/dist/auth/types.d.ts.map +0 -1
- package/dist/axios/index.d.ts +0 -4
- package/dist/axios/index.d.ts.map +0 -1
- package/dist/config/config.d.ts +0 -22
- package/dist/config/config.d.ts.map +0 -1
- package/dist/config/index.d.ts +0 -2
- package/dist/config/index.d.ts.map +0 -1
- package/dist/context/context.d.ts +0 -20
- package/dist/context/context.d.ts.map +0 -1
- package/dist/context/hook.d.ts +0 -4
- package/dist/context/hook.d.ts.map +0 -1
- package/dist/context/index.d.ts +0 -2
- package/dist/context/index.d.ts.map +0 -1
- package/dist/error/base.d.ts +0 -5
- package/dist/error/base.d.ts.map +0 -1
- package/dist/error/declaration.d.ts +0 -6
- package/dist/error/declaration.d.ts.map +0 -1
- package/dist/error/index.d.ts +0 -3
- package/dist/error/index.d.ts.map +0 -1
- package/dist/index.cjs.map +0 -1
- package/dist/index.d.ts.map +0 -1
- package/dist/index.modern.js +0 -2
- package/dist/index.modern.js.map +0 -1
- package/dist/index.module.js.map +0 -1
- package/dist/index.umd.js +0 -2
- package/dist/index.umd.js.map +0 -1
- package/dist/spec.d.ts +0 -14
- package/dist/spec.d.ts.map +0 -1
- package/dist/specification/index.d.ts +0 -3
- package/dist/specification/index.d.ts.map +0 -1
- package/dist/specification/slate.d.ts +0 -15
- package/dist/specification/slate.d.ts.map +0 -1
- package/dist/specification/specification.d.ts +0 -30
- package/dist/specification/specification.d.ts.map +0 -1
- package/dist/specification/zero.d.ts +0 -13
- package/dist/specification/zero.d.ts.map +0 -1
- package/dist/state.d.ts +0 -8
- package/dist/state.d.ts.map +0 -1
- package/dist/tokens.d.ts +0 -34
- package/dist/tokens.d.ts.map +0 -1
- package/dist/validation.d.ts +0 -5
- package/dist/validation.d.ts.map +0 -1
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.41",
|
|
4
4
|
"publishConfig": {
|
|
5
5
|
"access": "public"
|
|
6
6
|
},
|
|
@@ -11,7 +11,7 @@
|
|
|
11
11
|
"package.json"
|
|
12
12
|
],
|
|
13
13
|
"author": "Tobias Herber",
|
|
14
|
-
"license": "
|
|
14
|
+
"license": "FSL 1.1",
|
|
15
15
|
"type": "module",
|
|
16
16
|
"source": "src/index.ts",
|
|
17
17
|
"exports": {
|
|
@@ -23,22 +23,24 @@
|
|
|
23
23
|
"main": "./dist/index.cjs",
|
|
24
24
|
"module": "./dist/index.module.js",
|
|
25
25
|
"types": "dist/index.d.ts",
|
|
26
|
-
"unpkg": "./dist/index.
|
|
26
|
+
"unpkg": "./dist/index.module.js",
|
|
27
27
|
"scripts": {
|
|
28
28
|
"test": "vitest run --passWithNoTests",
|
|
29
|
-
"
|
|
30
|
-
"
|
|
29
|
+
"build": "tsup --config ../../tsup.packages.config.ts",
|
|
30
|
+
"typecheck": "tsc --noEmit",
|
|
31
|
+
"biome:check": "biome check --write src"
|
|
31
32
|
},
|
|
32
33
|
"dependencies": {
|
|
33
|
-
"@lowerdeck/error": "^1.0
|
|
34
|
-
"@slates/proto": "
|
|
35
|
-
"@slates/provider": "
|
|
34
|
+
"@lowerdeck/error": "^1.1.0",
|
|
35
|
+
"@slates/proto": "1.0.0-rc.26",
|
|
36
|
+
"@slates/provider": "1.0.0-rc.31",
|
|
37
|
+
"p-queue": "^6.6.2",
|
|
36
38
|
"zod": "^4.2.1"
|
|
37
39
|
},
|
|
38
40
|
"devDependencies": {
|
|
39
41
|
"microbundle": "^0.15.1",
|
|
40
|
-
"@slates/tsconfig": "
|
|
42
|
+
"@slates/tsconfig": "1.0.0-rc.1",
|
|
41
43
|
"typescript": "5.8.2",
|
|
42
44
|
"vitest": "^3.1.2"
|
|
43
45
|
}
|
|
44
|
-
}
|
|
46
|
+
}
|
|
@@ -0,0 +1,189 @@
|
|
|
1
|
+
import { SLATES_PROTOCOL_VERSION, SlatesProviderProtoHandlerManager } from '@slates/proto';
|
|
2
|
+
import { describe, expect, it, vi } from 'vitest';
|
|
3
|
+
import { z } from 'zod';
|
|
4
|
+
import { createProviderHandler } from './index';
|
|
5
|
+
|
|
6
|
+
let createManager = async (method: Record<string, any>) => {
|
|
7
|
+
let slate = {
|
|
8
|
+
spec: {
|
|
9
|
+
key: 'test',
|
|
10
|
+
name: 'Test',
|
|
11
|
+
auth: {
|
|
12
|
+
authStack: [
|
|
13
|
+
{
|
|
14
|
+
type: 'auth.oauth',
|
|
15
|
+
key: 'oauth',
|
|
16
|
+
name: 'OAuth',
|
|
17
|
+
scopes: [],
|
|
18
|
+
...method
|
|
19
|
+
}
|
|
20
|
+
],
|
|
21
|
+
outputSchema: z.object({ token: z.string() })
|
|
22
|
+
}
|
|
23
|
+
},
|
|
24
|
+
adapters: [],
|
|
25
|
+
actions: [],
|
|
26
|
+
triggerGroups: []
|
|
27
|
+
};
|
|
28
|
+
let manager = await createProviderHandler(slate as any, []).run();
|
|
29
|
+
|
|
30
|
+
await SlatesProviderProtoHandlerManager.handleInput(manager, {
|
|
31
|
+
jsonrpc: '2.0',
|
|
32
|
+
method: 'slates/hello',
|
|
33
|
+
params: { protocol: SLATES_PROTOCOL_VERSION }
|
|
34
|
+
});
|
|
35
|
+
await SlatesProviderProtoHandlerManager.handleInput(manager, {
|
|
36
|
+
jsonrpc: '2.0',
|
|
37
|
+
method: 'slates/participant.set',
|
|
38
|
+
params: { participants: [] }
|
|
39
|
+
});
|
|
40
|
+
|
|
41
|
+
return manager;
|
|
42
|
+
};
|
|
43
|
+
|
|
44
|
+
let baseParams = {
|
|
45
|
+
authenticationMethodId: 'oauth',
|
|
46
|
+
state: 'state-123',
|
|
47
|
+
redirectUri: 'https://hub.example/callback',
|
|
48
|
+
input: { tenant: 'acme' },
|
|
49
|
+
clientId: 'client-id',
|
|
50
|
+
clientSecret: 'client-secret',
|
|
51
|
+
scopes: ['read']
|
|
52
|
+
};
|
|
53
|
+
|
|
54
|
+
let getAdminAuthorizationUrl = (manager: SlatesProviderProtoHandlerManager) =>
|
|
55
|
+
SlatesProviderProtoHandlerManager.handleInput(manager, {
|
|
56
|
+
jsonrpc: '2.0',
|
|
57
|
+
id: 'url',
|
|
58
|
+
method: 'slates/auth.admin_authorization_url.get',
|
|
59
|
+
params: baseParams
|
|
60
|
+
});
|
|
61
|
+
|
|
62
|
+
let handleAdminCallback = (
|
|
63
|
+
manager: SlatesProviderProtoHandlerManager,
|
|
64
|
+
params: { code?: string; callbackParams?: Record<string, string> } = {}
|
|
65
|
+
) =>
|
|
66
|
+
SlatesProviderProtoHandlerManager.handleInput(manager, {
|
|
67
|
+
jsonrpc: '2.0',
|
|
68
|
+
id: 'callback',
|
|
69
|
+
method: 'slates/auth.admin_callback.handle',
|
|
70
|
+
params: { ...baseParams, ...params }
|
|
71
|
+
});
|
|
72
|
+
|
|
73
|
+
let regularMethod = () => ({
|
|
74
|
+
getAuthorizationUrl: vi.fn(async () => ({
|
|
75
|
+
url: 'https://provider.example/authorize',
|
|
76
|
+
callbackState: { verifier: 'regular' }
|
|
77
|
+
})),
|
|
78
|
+
handleCallback: vi.fn(async () => ({ output: { token: 'user-token' } }))
|
|
79
|
+
});
|
|
80
|
+
|
|
81
|
+
describe('slates/auth.admin_authorization_url.get', () => {
|
|
82
|
+
it('falls back to the regular authorization URL', async () => {
|
|
83
|
+
let method = regularMethod();
|
|
84
|
+
let manager = await createManager({ ...method, adminSetup: {} });
|
|
85
|
+
|
|
86
|
+
expect(await getAdminAuthorizationUrl(manager)).toEqual({
|
|
87
|
+
jsonrpc: '2.0',
|
|
88
|
+
id: 'url',
|
|
89
|
+
result: {
|
|
90
|
+
authorizationUrl: 'https://provider.example/authorize',
|
|
91
|
+
callbackState: { verifier: 'regular' }
|
|
92
|
+
}
|
|
93
|
+
});
|
|
94
|
+
let { authenticationMethodId: _, ...ctx } = baseParams;
|
|
95
|
+
expect(method.getAuthorizationUrl).toHaveBeenCalledWith({ ...ctx, config: {} });
|
|
96
|
+
});
|
|
97
|
+
|
|
98
|
+
it('uses the admin authorization URL override', async () => {
|
|
99
|
+
let method = regularMethod();
|
|
100
|
+
let adminGetAuthorizationUrl = vi.fn(async () => ({
|
|
101
|
+
url: 'https://provider.example/adminconsent',
|
|
102
|
+
input: { tenant: 'acme' }
|
|
103
|
+
}));
|
|
104
|
+
let manager = await createManager({
|
|
105
|
+
...method,
|
|
106
|
+
adminSetup: { getAuthorizationUrl: adminGetAuthorizationUrl }
|
|
107
|
+
});
|
|
108
|
+
|
|
109
|
+
expect(await getAdminAuthorizationUrl(manager)).toMatchObject({
|
|
110
|
+
result: {
|
|
111
|
+
authorizationUrl: 'https://provider.example/adminconsent',
|
|
112
|
+
input: { tenant: 'acme' }
|
|
113
|
+
}
|
|
114
|
+
});
|
|
115
|
+
expect(adminGetAuthorizationUrl).toHaveBeenCalledWith(
|
|
116
|
+
expect.objectContaining({ clientId: 'client-id', state: 'state-123', config: {} })
|
|
117
|
+
);
|
|
118
|
+
expect(method.getAuthorizationUrl).not.toHaveBeenCalled();
|
|
119
|
+
});
|
|
120
|
+
|
|
121
|
+
it('rejects methods without admin setup', async () => {
|
|
122
|
+
let manager = await createManager(regularMethod());
|
|
123
|
+
|
|
124
|
+
expect(await getAdminAuthorizationUrl(manager)).toMatchObject({
|
|
125
|
+
error: { code: 'request.precondition_failed' }
|
|
126
|
+
});
|
|
127
|
+
});
|
|
128
|
+
});
|
|
129
|
+
|
|
130
|
+
describe('slates/auth.admin_callback.handle', () => {
|
|
131
|
+
it('runs the regular callback and discards its output by default', async () => {
|
|
132
|
+
let method = regularMethod();
|
|
133
|
+
let manager = await createManager({ ...method, adminSetup: {} });
|
|
134
|
+
|
|
135
|
+
expect(await handleAdminCallback(manager, { code: 'admin-code' })).toEqual({
|
|
136
|
+
jsonrpc: '2.0',
|
|
137
|
+
id: 'callback',
|
|
138
|
+
result: {}
|
|
139
|
+
});
|
|
140
|
+
expect(method.handleCallback).toHaveBeenCalledWith(
|
|
141
|
+
expect.objectContaining({
|
|
142
|
+
code: 'admin-code',
|
|
143
|
+
clientId: 'client-id',
|
|
144
|
+
callbackParams: {},
|
|
145
|
+
callbackState: {},
|
|
146
|
+
config: {}
|
|
147
|
+
})
|
|
148
|
+
);
|
|
149
|
+
});
|
|
150
|
+
|
|
151
|
+
it('requires a code when falling back to the regular callback', async () => {
|
|
152
|
+
let method = regularMethod();
|
|
153
|
+
let manager = await createManager({ ...method, adminSetup: {} });
|
|
154
|
+
|
|
155
|
+
expect(await handleAdminCallback(manager)).toMatchObject({
|
|
156
|
+
error: { code: 'request.bad', message: expect.stringContaining('authorization code') }
|
|
157
|
+
});
|
|
158
|
+
expect(method.handleCallback).not.toHaveBeenCalled();
|
|
159
|
+
});
|
|
160
|
+
|
|
161
|
+
it('uses the admin callback override without a code', async () => {
|
|
162
|
+
let method = regularMethod();
|
|
163
|
+
let adminHandleCallback = vi.fn(async () => ({ instructions: '1. Trust the app.' }));
|
|
164
|
+
let manager = await createManager({
|
|
165
|
+
...method,
|
|
166
|
+
adminSetup: { handleCallback: adminHandleCallback }
|
|
167
|
+
});
|
|
168
|
+
|
|
169
|
+
expect(
|
|
170
|
+
await handleAdminCallback(manager, { callbackParams: { admin_consent: 'True' } })
|
|
171
|
+
).toMatchObject({ result: { instructions: '1. Trust the app.' } });
|
|
172
|
+
expect(adminHandleCallback).toHaveBeenCalledWith(
|
|
173
|
+
expect.objectContaining({
|
|
174
|
+
code: undefined,
|
|
175
|
+
callbackParams: { admin_consent: 'True' },
|
|
176
|
+
callbackState: {}
|
|
177
|
+
})
|
|
178
|
+
);
|
|
179
|
+
expect(method.handleCallback).not.toHaveBeenCalled();
|
|
180
|
+
});
|
|
181
|
+
|
|
182
|
+
it('rejects methods without admin setup', async () => {
|
|
183
|
+
let manager = await createManager(regularMethod());
|
|
184
|
+
|
|
185
|
+
expect(await handleAdminCallback(manager, { code: 'admin-code' })).toMatchObject({
|
|
186
|
+
error: { code: 'request.precondition_failed' }
|
|
187
|
+
});
|
|
188
|
+
});
|
|
189
|
+
});
|
|
@@ -0,0 +1,171 @@
|
|
|
1
|
+
import { SLATES_PROTOCOL_VERSION, SlatesProviderProtoHandlerManager } from '@slates/proto';
|
|
2
|
+
import {
|
|
3
|
+
Slate,
|
|
4
|
+
SlateAuth,
|
|
5
|
+
SlateConfig,
|
|
6
|
+
SlatePublicTool,
|
|
7
|
+
SlateSpecification
|
|
8
|
+
} from '@slates/provider';
|
|
9
|
+
import { afterEach, describe, expect, it, vi } from 'vitest';
|
|
10
|
+
import { z } from 'zod';
|
|
11
|
+
import { createProviderHandler } from './index';
|
|
12
|
+
|
|
13
|
+
let createManager = async () => {
|
|
14
|
+
let spec = SlateSpecification.create({
|
|
15
|
+
key: 'attachments-test',
|
|
16
|
+
name: 'Attachments Test',
|
|
17
|
+
config: SlateConfig.create(z.object({})),
|
|
18
|
+
auth: SlateAuth.create().output(z.object({}))
|
|
19
|
+
});
|
|
20
|
+
let tool = SlatePublicTool.create(spec, {
|
|
21
|
+
key: 'attach',
|
|
22
|
+
name: 'Attach'
|
|
23
|
+
})
|
|
24
|
+
.input(z.object({}))
|
|
25
|
+
.output(z.object({}))
|
|
26
|
+
.handleInvocation(async ctx => {
|
|
27
|
+
await ctx.addAttachment({
|
|
28
|
+
type: 'content',
|
|
29
|
+
content: new Response('hello', {
|
|
30
|
+
headers: { 'content-type': 'text/plain' }
|
|
31
|
+
}),
|
|
32
|
+
filename: 'hello.txt'
|
|
33
|
+
});
|
|
34
|
+
|
|
35
|
+
return { output: {}, message: 'ok' };
|
|
36
|
+
})
|
|
37
|
+
.build();
|
|
38
|
+
let slate = Slate.create({ spec, triggers: [], tools: [tool] });
|
|
39
|
+
let manager = await createProviderHandler(slate, []).run();
|
|
40
|
+
|
|
41
|
+
await SlatesProviderProtoHandlerManager.handleInput(manager, {
|
|
42
|
+
jsonrpc: '2.0',
|
|
43
|
+
method: 'slates/hello',
|
|
44
|
+
params: { protocol: SLATES_PROTOCOL_VERSION }
|
|
45
|
+
});
|
|
46
|
+
await SlatesProviderProtoHandlerManager.handleInput(manager, {
|
|
47
|
+
jsonrpc: '2.0',
|
|
48
|
+
method: 'slates/participant.set',
|
|
49
|
+
params: {
|
|
50
|
+
participants: [
|
|
51
|
+
{ type: 'consumer', id: 'consumer', name: 'Consumer' },
|
|
52
|
+
{ type: 'hub', id: 'hub', name: 'Hub' }
|
|
53
|
+
]
|
|
54
|
+
}
|
|
55
|
+
});
|
|
56
|
+
|
|
57
|
+
return manager;
|
|
58
|
+
};
|
|
59
|
+
|
|
60
|
+
let invoke = async (manager: SlatesProviderProtoHandlerManager) =>
|
|
61
|
+
SlatesProviderProtoHandlerManager.handleInput(manager, {
|
|
62
|
+
jsonrpc: '2.0',
|
|
63
|
+
id: 'request',
|
|
64
|
+
method: 'slates/action.tool.invoke',
|
|
65
|
+
params: { actionId: 'attach', input: {} }
|
|
66
|
+
});
|
|
67
|
+
|
|
68
|
+
let expectInlineAttachment = (response: Awaited<ReturnType<typeof invoke>>) => {
|
|
69
|
+
expect(response).toMatchObject({
|
|
70
|
+
result: {
|
|
71
|
+
attachments: [
|
|
72
|
+
{
|
|
73
|
+
mimeType: 'text/plain',
|
|
74
|
+
content: {
|
|
75
|
+
type: 'content',
|
|
76
|
+
encoding: 'base64',
|
|
77
|
+
content: Buffer.from('hello').toString('base64')
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
]
|
|
81
|
+
}
|
|
82
|
+
});
|
|
83
|
+
};
|
|
84
|
+
|
|
85
|
+
describe('addAttachment fallback', () => {
|
|
86
|
+
afterEach(() => {
|
|
87
|
+
vi.restoreAllMocks();
|
|
88
|
+
});
|
|
89
|
+
|
|
90
|
+
it('returns a standard inline attachment when capabilities and a live token are absent', async () => {
|
|
91
|
+
let fetchSpy = vi.spyOn(globalThis, 'fetch');
|
|
92
|
+
let response = await invoke(await createManager());
|
|
93
|
+
|
|
94
|
+
expectInlineAttachment(response);
|
|
95
|
+
expect(fetchSpy).not.toHaveBeenCalled();
|
|
96
|
+
});
|
|
97
|
+
|
|
98
|
+
it('returns a standard inline attachment when direct upload is enabled without a live token', async () => {
|
|
99
|
+
let manager = await createManager();
|
|
100
|
+
let fetchSpy = vi.spyOn(globalThis, 'fetch');
|
|
101
|
+
|
|
102
|
+
await SlatesProviderProtoHandlerManager.handleInput(manager, {
|
|
103
|
+
jsonrpc: '2.0',
|
|
104
|
+
method: 'slates/hub.capabilities.set',
|
|
105
|
+
params: { capabilities: { attachments: { directUpload: { enabled: true } } } }
|
|
106
|
+
});
|
|
107
|
+
|
|
108
|
+
expectInlineAttachment(await invoke(manager));
|
|
109
|
+
expect(fetchSpy).not.toHaveBeenCalled();
|
|
110
|
+
});
|
|
111
|
+
|
|
112
|
+
it('returns a standard inline attachment when a live token exists without the capability', async () => {
|
|
113
|
+
let manager = await createManager();
|
|
114
|
+
let fetchSpy = vi.spyOn(globalThis, 'fetch');
|
|
115
|
+
|
|
116
|
+
await SlatesProviderProtoHandlerManager.handleInput(manager, {
|
|
117
|
+
jsonrpc: '2.0',
|
|
118
|
+
method: 'slates/hub.live_invocation.set',
|
|
119
|
+
params: { token: 'token', baseUrl: 'https://hub.example' }
|
|
120
|
+
});
|
|
121
|
+
|
|
122
|
+
expectInlineAttachment(await invoke(manager));
|
|
123
|
+
expect(fetchSpy).not.toHaveBeenCalled();
|
|
124
|
+
});
|
|
125
|
+
|
|
126
|
+
it('uses direct upload only when both the capability and live token exist', async () => {
|
|
127
|
+
let manager = await createManager();
|
|
128
|
+
let fetchSpy = vi
|
|
129
|
+
.spyOn(globalThis, 'fetch')
|
|
130
|
+
.mockResolvedValueOnce(
|
|
131
|
+
new Response(
|
|
132
|
+
JSON.stringify({
|
|
133
|
+
attachments: [
|
|
134
|
+
{
|
|
135
|
+
referenceId: 'attachment-reference',
|
|
136
|
+
uploadUrl: 'https://uploads.example/attachment'
|
|
137
|
+
}
|
|
138
|
+
]
|
|
139
|
+
}),
|
|
140
|
+
{ status: 200, headers: { 'content-type': 'application/json' } }
|
|
141
|
+
)
|
|
142
|
+
)
|
|
143
|
+
.mockResolvedValueOnce(new Response(null, { status: 200 }));
|
|
144
|
+
|
|
145
|
+
await SlatesProviderProtoHandlerManager.handleInput(manager, {
|
|
146
|
+
jsonrpc: '2.0',
|
|
147
|
+
method: 'slates/hub.capabilities.set',
|
|
148
|
+
params: { capabilities: { attachments: { directUpload: { enabled: true } } } }
|
|
149
|
+
});
|
|
150
|
+
await SlatesProviderProtoHandlerManager.handleInput(manager, {
|
|
151
|
+
jsonrpc: '2.0',
|
|
152
|
+
method: 'slates/hub.live_invocation.set',
|
|
153
|
+
params: { token: 'token', baseUrl: 'https://hub.example' }
|
|
154
|
+
});
|
|
155
|
+
|
|
156
|
+
expect(await invoke(manager)).toMatchObject({
|
|
157
|
+
result: {
|
|
158
|
+
attachments: [
|
|
159
|
+
{
|
|
160
|
+
mimeType: 'text/plain',
|
|
161
|
+
content: {
|
|
162
|
+
type: 'upload_reference',
|
|
163
|
+
referenceId: 'attachment-reference'
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
]
|
|
167
|
+
}
|
|
168
|
+
});
|
|
169
|
+
expect(fetchSpy).toHaveBeenCalledTimes(2);
|
|
170
|
+
});
|
|
171
|
+
});
|
|
@@ -0,0 +1,216 @@
|
|
|
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
|
+
|
|
162
|
+
it('returns empty adapter and trigger group lists for older slates that omit those collections', async () => {
|
|
163
|
+
let manager = await createProviderHandler(
|
|
164
|
+
{
|
|
165
|
+
spec: { key: 'legacy', name: 'Legacy' },
|
|
166
|
+
actions: [
|
|
167
|
+
{
|
|
168
|
+
key: 'provider_tool',
|
|
169
|
+
type: 'tool',
|
|
170
|
+
name: 'Action',
|
|
171
|
+
description: undefined,
|
|
172
|
+
instructions: undefined,
|
|
173
|
+
constraints: undefined,
|
|
174
|
+
tags: [],
|
|
175
|
+
metadata: {},
|
|
176
|
+
scopes: [],
|
|
177
|
+
authMethods: [],
|
|
178
|
+
docs: [],
|
|
179
|
+
inputSchema: z.object({}),
|
|
180
|
+
outputSchema: z.object({}),
|
|
181
|
+
isPublic: false
|
|
182
|
+
}
|
|
183
|
+
]
|
|
184
|
+
} as any,
|
|
185
|
+
[]
|
|
186
|
+
).run();
|
|
187
|
+
|
|
188
|
+
await SlatesProviderProtoHandlerManager.handleInput(manager, {
|
|
189
|
+
jsonrpc: '2.0',
|
|
190
|
+
method: 'slates/hello',
|
|
191
|
+
params: { protocol: SLATES_PROTOCOL_VERSION }
|
|
192
|
+
});
|
|
193
|
+
await SlatesProviderProtoHandlerManager.handleInput(manager, {
|
|
194
|
+
jsonrpc: '2.0',
|
|
195
|
+
method: 'slates/participant.set',
|
|
196
|
+
params: { participants: [] }
|
|
197
|
+
});
|
|
198
|
+
await SlatesProviderProtoHandlerManager.handleInput(manager, {
|
|
199
|
+
jsonrpc: '2.0',
|
|
200
|
+
method: 'slates/hub.capabilities.set',
|
|
201
|
+
params: { capabilities: { adapters: true, triggers: true } }
|
|
202
|
+
});
|
|
203
|
+
|
|
204
|
+
expect(await request(manager, 'slates/adapters.list')).toMatchObject({
|
|
205
|
+
result: { adapters: [] }
|
|
206
|
+
});
|
|
207
|
+
expect(await request(manager, 'slates/trigger_groups.list')).toMatchObject({
|
|
208
|
+
result: { triggerGroups: [] }
|
|
209
|
+
});
|
|
210
|
+
expect(
|
|
211
|
+
await request(manager, 'slates/actions.list', { includeAdapterActions: true })
|
|
212
|
+
).toMatchObject({
|
|
213
|
+
result: { actions: [{ id: 'provider_tool' }] }
|
|
214
|
+
});
|
|
215
|
+
});
|
|
216
|
+
});
|