@slates/profiles 1.0.0-rc.2
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 +445 -0
- package/dist/index.d.cts +122 -0
- package/dist/index.d.ts +122 -0
- package/dist/index.module.js +403 -0
- package/package.json +44 -0
- package/src/client.ts +143 -0
- package/src/index.ts +3 -0
- package/src/store.test.ts +315 -0
- package/src/store.ts +422 -0
- package/src/types.ts +67 -0
|
@@ -0,0 +1,315 @@
|
|
|
1
|
+
import { mkdir, mkdtemp, rm, writeFile } from 'fs/promises';
|
|
2
|
+
import { tmpdir } from 'os';
|
|
3
|
+
import path from 'path';
|
|
4
|
+
import { afterEach, describe, expect, it } from 'vitest';
|
|
5
|
+
import { createSlatesClientFromProfile } from './client';
|
|
6
|
+
import { SlatesCliStore } from './store';
|
|
7
|
+
|
|
8
|
+
let tempDirs: string[] = [];
|
|
9
|
+
|
|
10
|
+
let createTempDir = async () => {
|
|
11
|
+
let dir = await mkdtemp(path.join(tmpdir(), 'slates-profiles-'));
|
|
12
|
+
tempDirs.push(dir);
|
|
13
|
+
return dir;
|
|
14
|
+
};
|
|
15
|
+
|
|
16
|
+
afterEach(async () => {
|
|
17
|
+
await Promise.all(tempDirs.splice(0).map(dir => rm(dir, { recursive: true, force: true })));
|
|
18
|
+
});
|
|
19
|
+
|
|
20
|
+
describe('@slates/profiles store', () => {
|
|
21
|
+
it('creates an integration-scoped store with profiles and auth', async () => {
|
|
22
|
+
let cwd = await createTempDir();
|
|
23
|
+
let store = await SlatesCliStore.open({
|
|
24
|
+
cwd,
|
|
25
|
+
scope: {
|
|
26
|
+
key: 'integrations/demo',
|
|
27
|
+
name: 'demo'
|
|
28
|
+
}
|
|
29
|
+
});
|
|
30
|
+
|
|
31
|
+
let profile = store.upsertProfile({
|
|
32
|
+
name: 'Demo',
|
|
33
|
+
target: {
|
|
34
|
+
type: 'local',
|
|
35
|
+
entry: './fixtures/demo-slate.mjs',
|
|
36
|
+
exportName: 'provider'
|
|
37
|
+
}
|
|
38
|
+
});
|
|
39
|
+
|
|
40
|
+
store.setCurrentProfile(profile.id);
|
|
41
|
+
store.setProfileConfig(profile.id, { prefix: 'Hello' });
|
|
42
|
+
store.upsertAuth(profile.id, {
|
|
43
|
+
authMethodId: 'token_auth',
|
|
44
|
+
authType: 'auth.token',
|
|
45
|
+
input: { token: 'abc' },
|
|
46
|
+
output: { token: 'abc' },
|
|
47
|
+
scopes: []
|
|
48
|
+
});
|
|
49
|
+
await store.save();
|
|
50
|
+
|
|
51
|
+
let reopened = await SlatesCliStore.open({ storePath: store.storePath });
|
|
52
|
+
let reopenedProfile = reopened.requireProfile(profile.id);
|
|
53
|
+
|
|
54
|
+
expect(reopened.getProfile()?.id).toBe(profile.id);
|
|
55
|
+
expect(reopenedProfile.config).toEqual({ prefix: 'Hello' });
|
|
56
|
+
expect(reopened.getAuth(profile.id, 'token_auth')?.output).toEqual({ token: 'abc' });
|
|
57
|
+
expect(reopened.scope?.key).toBe('integrations/demo');
|
|
58
|
+
expect(reopened.storePath).toBe(path.join(cwd, '.slates-cli', 'profiles', 'integrations', 'demo', 'store.json'));
|
|
59
|
+
});
|
|
60
|
+
|
|
61
|
+
it('migrates matching legacy profiles into an integration-scoped store', async () => {
|
|
62
|
+
let cwd = await createTempDir();
|
|
63
|
+
let cliDir = path.join(cwd, '.slates-cli');
|
|
64
|
+
await mkdir(cliDir, { recursive: true });
|
|
65
|
+
|
|
66
|
+
await writeFile(
|
|
67
|
+
path.join(cliDir, 'store.json'),
|
|
68
|
+
JSON.stringify(
|
|
69
|
+
{
|
|
70
|
+
version: 1,
|
|
71
|
+
currentProfileId: 'demo-profile',
|
|
72
|
+
profiles: {
|
|
73
|
+
'demo-profile': {
|
|
74
|
+
id: 'demo-profile',
|
|
75
|
+
name: 'Demo',
|
|
76
|
+
target: {
|
|
77
|
+
type: 'local',
|
|
78
|
+
entry: 'integrations/demo/src/index.ts',
|
|
79
|
+
exportName: 'provider'
|
|
80
|
+
},
|
|
81
|
+
config: { prefix: 'Hello' },
|
|
82
|
+
auth: {},
|
|
83
|
+
session: null,
|
|
84
|
+
metadata: {
|
|
85
|
+
provider: null,
|
|
86
|
+
actions: null
|
|
87
|
+
},
|
|
88
|
+
createdAt: '2026-01-01T00:00:00.000Z',
|
|
89
|
+
updatedAt: '2026-01-01T00:00:00.000Z'
|
|
90
|
+
},
|
|
91
|
+
'other-profile': {
|
|
92
|
+
id: 'other-profile',
|
|
93
|
+
name: 'Other',
|
|
94
|
+
target: {
|
|
95
|
+
type: 'local',
|
|
96
|
+
entry: 'integrations/other/src/index.ts',
|
|
97
|
+
exportName: 'provider'
|
|
98
|
+
},
|
|
99
|
+
config: null,
|
|
100
|
+
auth: {},
|
|
101
|
+
session: null,
|
|
102
|
+
metadata: {
|
|
103
|
+
provider: null,
|
|
104
|
+
actions: null
|
|
105
|
+
},
|
|
106
|
+
createdAt: '2026-01-01T00:00:00.000Z',
|
|
107
|
+
updatedAt: '2026-01-01T00:00:00.000Z'
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
},
|
|
111
|
+
null,
|
|
112
|
+
2
|
|
113
|
+
),
|
|
114
|
+
'utf-8'
|
|
115
|
+
);
|
|
116
|
+
|
|
117
|
+
let store = await SlatesCliStore.open({
|
|
118
|
+
cwd,
|
|
119
|
+
scope: {
|
|
120
|
+
key: 'integrations/demo',
|
|
121
|
+
name: 'demo'
|
|
122
|
+
}
|
|
123
|
+
});
|
|
124
|
+
|
|
125
|
+
expect(store.listProfiles().map(profile => profile.id)).toEqual(['demo-profile']);
|
|
126
|
+
expect(store.getProfile()?.config).toEqual({ prefix: 'Hello' });
|
|
127
|
+
expect(store.listOAuthCredentials()).toEqual([]);
|
|
128
|
+
});
|
|
129
|
+
|
|
130
|
+
it('creates a local client from a profile target', async () => {
|
|
131
|
+
let cwd = await createTempDir();
|
|
132
|
+
let entry = path.join(cwd, 'demo-slate.mjs');
|
|
133
|
+
await writeFile(
|
|
134
|
+
entry,
|
|
135
|
+
`
|
|
136
|
+
import { Slate, SlateAuth, SlateConfig, SlateSpecification } from '@slates/provider';
|
|
137
|
+
import { z } from 'zod';
|
|
138
|
+
|
|
139
|
+
let config = SlateConfig.create(
|
|
140
|
+
z.object({
|
|
141
|
+
prefix: z.string().optional()
|
|
142
|
+
})
|
|
143
|
+
).getDefaultConfig(() => ({}));
|
|
144
|
+
|
|
145
|
+
let auth = SlateAuth.create();
|
|
146
|
+
|
|
147
|
+
export let provider = Slate.create({
|
|
148
|
+
spec: SlateSpecification.create({
|
|
149
|
+
key: 'demo',
|
|
150
|
+
name: 'Demo Slate',
|
|
151
|
+
description: 'Local test slate',
|
|
152
|
+
config,
|
|
153
|
+
auth
|
|
154
|
+
}),
|
|
155
|
+
tools: [],
|
|
156
|
+
triggers: []
|
|
157
|
+
});
|
|
158
|
+
`,
|
|
159
|
+
'utf-8'
|
|
160
|
+
);
|
|
161
|
+
|
|
162
|
+
let store = await SlatesCliStore.open({
|
|
163
|
+
cwd,
|
|
164
|
+
scope: {
|
|
165
|
+
key: 'integrations/demo',
|
|
166
|
+
name: 'demo'
|
|
167
|
+
}
|
|
168
|
+
});
|
|
169
|
+
let profile = store.upsertProfile({
|
|
170
|
+
name: 'Demo',
|
|
171
|
+
target: {
|
|
172
|
+
type: 'local',
|
|
173
|
+
entry: './demo-slate.mjs',
|
|
174
|
+
exportName: 'provider'
|
|
175
|
+
}
|
|
176
|
+
});
|
|
177
|
+
|
|
178
|
+
let client = await createSlatesClientFromProfile(profile, { cwd });
|
|
179
|
+
let provider = await client.identify();
|
|
180
|
+
|
|
181
|
+
expect(provider.provider.id).toBe('demo');
|
|
182
|
+
expect(provider.provider.name).toBe('Demo Slate');
|
|
183
|
+
});
|
|
184
|
+
|
|
185
|
+
it('stores OAuth client credentials per integration', async () => {
|
|
186
|
+
let cwd = await createTempDir();
|
|
187
|
+
let store = await SlatesCliStore.open({
|
|
188
|
+
cwd,
|
|
189
|
+
scope: {
|
|
190
|
+
key: 'integrations/demo',
|
|
191
|
+
name: 'demo'
|
|
192
|
+
}
|
|
193
|
+
});
|
|
194
|
+
|
|
195
|
+
let credential = store.upsertOAuthCredential({
|
|
196
|
+
name: 'Primary OAuth App',
|
|
197
|
+
authMethodId: 'oauth',
|
|
198
|
+
clientId: 'client-id',
|
|
199
|
+
clientSecret: 'client-secret'
|
|
200
|
+
});
|
|
201
|
+
await store.save();
|
|
202
|
+
|
|
203
|
+
let reopened = await SlatesCliStore.open({ storePath: store.storePath });
|
|
204
|
+
expect(reopened.getOAuthCredential(credential.id)?.clientId).toBe('client-id');
|
|
205
|
+
expect(reopened.getOAuthCredential('Primary OAuth App')?.clientSecret).toBe('client-secret');
|
|
206
|
+
});
|
|
207
|
+
|
|
208
|
+
it('refreshes expired OAuth auth automatically in the profile-backed client', async () => {
|
|
209
|
+
let cwd = await createTempDir();
|
|
210
|
+
let entry = path.join(cwd, 'oauth-slate.mjs');
|
|
211
|
+
await writeFile(
|
|
212
|
+
entry,
|
|
213
|
+
`
|
|
214
|
+
import { Slate, SlateAuth, SlateConfig, SlateSpecification, SlateTool } from '@slates/provider';
|
|
215
|
+
import { z } from 'zod';
|
|
216
|
+
|
|
217
|
+
let config = SlateConfig.create(z.object({})).getDefaultConfig(() => ({}));
|
|
218
|
+
let auth = SlateAuth.create()
|
|
219
|
+
.output(
|
|
220
|
+
z.object({
|
|
221
|
+
token: z.string(),
|
|
222
|
+
refreshToken: z.string().optional(),
|
|
223
|
+
expiresAt: z.string().optional()
|
|
224
|
+
})
|
|
225
|
+
)
|
|
226
|
+
.addOauth({
|
|
227
|
+
type: 'auth.oauth',
|
|
228
|
+
key: 'oauth',
|
|
229
|
+
name: 'OAuth',
|
|
230
|
+
scopes: [],
|
|
231
|
+
getAuthorizationUrl: async () => ({ url: 'https://example.com' }),
|
|
232
|
+
handleCallback: async () => ({ output: { token: 'initial-token' } }),
|
|
233
|
+
handleTokenRefresh: async ctx => ({
|
|
234
|
+
output: {
|
|
235
|
+
token: \`\${ctx.output.token}-refreshed\`,
|
|
236
|
+
refreshToken: ctx.output.refreshToken,
|
|
237
|
+
expiresAt: new Date(Date.now() + 60 * 60 * 1000).toISOString()
|
|
238
|
+
}
|
|
239
|
+
})
|
|
240
|
+
});
|
|
241
|
+
|
|
242
|
+
let spec = SlateSpecification.create({
|
|
243
|
+
key: 'oauth-demo',
|
|
244
|
+
name: 'OAuth Demo',
|
|
245
|
+
description: 'OAuth test slate',
|
|
246
|
+
config,
|
|
247
|
+
auth
|
|
248
|
+
});
|
|
249
|
+
|
|
250
|
+
let tool = SlateTool.create(spec, {
|
|
251
|
+
key: 'whoami',
|
|
252
|
+
name: 'Who Am I'
|
|
253
|
+
})
|
|
254
|
+
.input(z.object({}))
|
|
255
|
+
.output(
|
|
256
|
+
z.object({
|
|
257
|
+
token: z.string()
|
|
258
|
+
})
|
|
259
|
+
)
|
|
260
|
+
.handleInvocation(async ctx => ({
|
|
261
|
+
output: {
|
|
262
|
+
token: ctx.auth.token
|
|
263
|
+
}
|
|
264
|
+
}))
|
|
265
|
+
.build();
|
|
266
|
+
|
|
267
|
+
export let provider = Slate.create({
|
|
268
|
+
spec,
|
|
269
|
+
tools: [tool],
|
|
270
|
+
triggers: []
|
|
271
|
+
});
|
|
272
|
+
`,
|
|
273
|
+
'utf-8'
|
|
274
|
+
);
|
|
275
|
+
|
|
276
|
+
let store = await SlatesCliStore.open({
|
|
277
|
+
cwd,
|
|
278
|
+
scope: {
|
|
279
|
+
key: 'integrations/demo',
|
|
280
|
+
name: 'demo'
|
|
281
|
+
}
|
|
282
|
+
});
|
|
283
|
+
let profile = store.upsertProfile({
|
|
284
|
+
name: 'Demo',
|
|
285
|
+
target: {
|
|
286
|
+
type: 'local',
|
|
287
|
+
entry: './oauth-slate.mjs',
|
|
288
|
+
exportName: 'provider'
|
|
289
|
+
}
|
|
290
|
+
});
|
|
291
|
+
store.setProfileConfig(profile.id, {});
|
|
292
|
+
store.upsertAuth(profile.id, {
|
|
293
|
+
authMethodId: 'oauth',
|
|
294
|
+
authType: 'auth.oauth',
|
|
295
|
+
input: {},
|
|
296
|
+
output: {
|
|
297
|
+
token: 'expired-token',
|
|
298
|
+
refreshToken: 'refresh-token',
|
|
299
|
+
expiresAt: new Date(Date.now() - 60 * 1000).toISOString()
|
|
300
|
+
},
|
|
301
|
+
scopes: [],
|
|
302
|
+
clientId: 'client-id',
|
|
303
|
+
clientSecret: 'client-secret'
|
|
304
|
+
});
|
|
305
|
+
await store.save();
|
|
306
|
+
|
|
307
|
+
let client = await createSlatesClientFromProfile(profile, { cwd, store });
|
|
308
|
+
let result = await client.invokeTool('whoami', {});
|
|
309
|
+
|
|
310
|
+
expect(result.output).toEqual({ token: 'expired-token-refreshed' });
|
|
311
|
+
expect(store.getAuth(profile.id, 'oauth')?.output).toMatchObject({
|
|
312
|
+
token: 'expired-token-refreshed'
|
|
313
|
+
});
|
|
314
|
+
});
|
|
315
|
+
});
|