@slates/cli 1.0.0-rc.10
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/package.json +38 -0
- package/src/cli.ts +291 -0
- package/src/commands/auth.test.ts +60 -0
- package/src/commands/auth.ts +451 -0
- package/src/commands/config.ts +31 -0
- package/src/commands/index.ts +6 -0
- package/src/commands/profiles.ts +151 -0
- package/src/commands/repl.ts +95 -0
- package/src/commands/test.ts +94 -0
- package/src/commands/tools.ts +57 -0
- package/src/lib/context.ts +199 -0
- package/src/lib/integration.test.ts +84 -0
- package/src/lib/integration.ts +152 -0
- package/src/lib/oauth.ts +162 -0
- package/src/lib/prompts.ts +159 -0
- package/src/lib/types.ts +10 -0
|
@@ -0,0 +1,451 @@
|
|
|
1
|
+
import { confirm, select } from '@inquirer/prompts';
|
|
2
|
+
import {
|
|
3
|
+
normalizeMicrosoftRedirectUri,
|
|
4
|
+
normalizeMicrosoftRedirectUriForIntegration
|
|
5
|
+
} from '@slates/oauth-microsoft';
|
|
6
|
+
import type { SlatesOAuthCredentialRecord, SlatesStoredAuth } from '@slates/profiles';
|
|
7
|
+
import {
|
|
8
|
+
chooseAuthMethod,
|
|
9
|
+
createClientContext,
|
|
10
|
+
createIntegrationClientContext,
|
|
11
|
+
openIntegrationStore
|
|
12
|
+
} from '../lib/context';
|
|
13
|
+
import { chooseScopes, createOAuthCallbackListener, printBrowserUrl } from '../lib/oauth';
|
|
14
|
+
import {
|
|
15
|
+
parseJsonObject,
|
|
16
|
+
parseList,
|
|
17
|
+
promptForObjectSchema,
|
|
18
|
+
promptForString
|
|
19
|
+
} from '../lib/prompts';
|
|
20
|
+
import type { JsonInput, WithProfile } from '../lib/types';
|
|
21
|
+
|
|
22
|
+
type JsonObject = Record<string, any>;
|
|
23
|
+
let NOTION_INTEGRATION_KEY = 'notion';
|
|
24
|
+
let SALESFORCE_INTEGRATION_KEY = 'salesforce';
|
|
25
|
+
let INTERCOM_INTEGRATION_KEY = 'intercom';
|
|
26
|
+
let TYPEFORM_INTEGRATION_KEY = 'typeform';
|
|
27
|
+
let XERO_INTEGRATION_KEY = 'xero';
|
|
28
|
+
let ZENDESK_INTEGRATION_KEY = 'zendesk';
|
|
29
|
+
let HUBSPOT_INTEGRATION_KEY = 'hubspot';
|
|
30
|
+
let HUBSPOT_DEVELOPER_PLATFORM_OAUTH_METHOD_ID = 'developer_platform_oauth';
|
|
31
|
+
let LOOPBACK_REDIRECT_NORMALIZED_INTEGRATIONS = new Set([
|
|
32
|
+
INTERCOM_INTEGRATION_KEY,
|
|
33
|
+
NOTION_INTEGRATION_KEY,
|
|
34
|
+
SALESFORCE_INTEGRATION_KEY,
|
|
35
|
+
TYPEFORM_INTEGRATION_KEY,
|
|
36
|
+
XERO_INTEGRATION_KEY,
|
|
37
|
+
ZENDESK_INTEGRATION_KEY
|
|
38
|
+
]);
|
|
39
|
+
|
|
40
|
+
type AuthSetupOptions = WithProfile &
|
|
41
|
+
JsonInput & {
|
|
42
|
+
authMethodId?: string;
|
|
43
|
+
clientId?: string;
|
|
44
|
+
clientSecret?: string;
|
|
45
|
+
oauthCredential?: string;
|
|
46
|
+
scopes?: string;
|
|
47
|
+
};
|
|
48
|
+
|
|
49
|
+
export let normalizeCallbackRedirectUriForIntegration = (
|
|
50
|
+
integration: string,
|
|
51
|
+
redirectUri: string,
|
|
52
|
+
authMethodId?: string
|
|
53
|
+
) => {
|
|
54
|
+
if (
|
|
55
|
+
LOOPBACK_REDIRECT_NORMALIZED_INTEGRATIONS.has(integration) ||
|
|
56
|
+
(integration === HUBSPOT_INTEGRATION_KEY &&
|
|
57
|
+
authMethodId === HUBSPOT_DEVELOPER_PLATFORM_OAUTH_METHOD_ID)
|
|
58
|
+
) {
|
|
59
|
+
return normalizeMicrosoftRedirectUri(redirectUri);
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
return normalizeMicrosoftRedirectUriForIntegration(integration, redirectUri);
|
|
63
|
+
};
|
|
64
|
+
|
|
65
|
+
export let listAuth = async (opts: WithProfile) => {
|
|
66
|
+
let { store, profile } = await createClientContext(opts);
|
|
67
|
+
return store.listAuth(profile.id);
|
|
68
|
+
};
|
|
69
|
+
|
|
70
|
+
export let getAuth = async (opts: WithProfile & { authMethodId?: string }) => {
|
|
71
|
+
let { store, profile } = await createClientContext(opts);
|
|
72
|
+
return store.getAuth(profile.id, opts.authMethodId);
|
|
73
|
+
};
|
|
74
|
+
|
|
75
|
+
export let listOAuthCredentials = async (
|
|
76
|
+
opts: Pick<WithProfile, 'integration'> & { authMethodId?: string }
|
|
77
|
+
) => {
|
|
78
|
+
let { store } = await openIntegrationStore(opts.integration);
|
|
79
|
+
return store.listOAuthCredentials(opts.authMethodId).map(credential => ({
|
|
80
|
+
id: credential.id,
|
|
81
|
+
name: credential.name,
|
|
82
|
+
authMethodId: credential.authMethodId,
|
|
83
|
+
clientId: credential.clientId
|
|
84
|
+
}));
|
|
85
|
+
};
|
|
86
|
+
|
|
87
|
+
export let addOAuthCredentials = async (
|
|
88
|
+
opts: Pick<WithProfile, 'integration'> & {
|
|
89
|
+
authMethodId?: string;
|
|
90
|
+
name?: string;
|
|
91
|
+
clientId?: string;
|
|
92
|
+
clientSecret?: string;
|
|
93
|
+
}
|
|
94
|
+
): Promise<SlatesOAuthCredentialRecord> => {
|
|
95
|
+
let { store, client } = await createIntegrationClientContext({
|
|
96
|
+
integration: opts.integration
|
|
97
|
+
});
|
|
98
|
+
let authMethod = await chooseAuthMethod({
|
|
99
|
+
client,
|
|
100
|
+
authMethodId: opts.authMethodId,
|
|
101
|
+
forcePrompt: !opts.authMethodId
|
|
102
|
+
});
|
|
103
|
+
|
|
104
|
+
if (authMethod.type !== 'auth.oauth') {
|
|
105
|
+
throw new Error(`Authentication method ${authMethod.id} is not OAuth.`);
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
let clientId = opts.clientId ?? (await promptForString({ message: 'OAuth client ID' }));
|
|
109
|
+
let clientSecret =
|
|
110
|
+
opts.clientSecret ??
|
|
111
|
+
(await promptForString({ message: 'OAuth client secret', secret: true }));
|
|
112
|
+
let name =
|
|
113
|
+
opts.name ??
|
|
114
|
+
(await promptForString({
|
|
115
|
+
message: 'Credential name',
|
|
116
|
+
defaultValue: `${authMethod.name} credentials`
|
|
117
|
+
}));
|
|
118
|
+
|
|
119
|
+
let credential = store.upsertOAuthCredential({
|
|
120
|
+
name,
|
|
121
|
+
authMethodId: authMethod.id,
|
|
122
|
+
clientId,
|
|
123
|
+
clientSecret
|
|
124
|
+
});
|
|
125
|
+
await store.save();
|
|
126
|
+
return credential;
|
|
127
|
+
};
|
|
128
|
+
|
|
129
|
+
let createOAuthCredentialInteractive = async (opts: {
|
|
130
|
+
store: Awaited<ReturnType<typeof createClientContext>>['store'];
|
|
131
|
+
authMethod: { id: string; name: string; type: string };
|
|
132
|
+
clientId?: string;
|
|
133
|
+
clientSecret?: string;
|
|
134
|
+
}) => {
|
|
135
|
+
let clientId = opts.clientId ?? (await promptForString({ message: 'OAuth client ID' }));
|
|
136
|
+
let clientSecret =
|
|
137
|
+
opts.clientSecret ??
|
|
138
|
+
(await promptForString({ message: 'OAuth client secret', secret: true }));
|
|
139
|
+
let name = await promptForString({
|
|
140
|
+
message: 'Credential name',
|
|
141
|
+
defaultValue: `${opts.authMethod.name} credentials`
|
|
142
|
+
});
|
|
143
|
+
|
|
144
|
+
let credential = opts.store.upsertOAuthCredential({
|
|
145
|
+
name,
|
|
146
|
+
authMethodId: opts.authMethod.id,
|
|
147
|
+
clientId,
|
|
148
|
+
clientSecret
|
|
149
|
+
});
|
|
150
|
+
await opts.store.save();
|
|
151
|
+
return credential;
|
|
152
|
+
};
|
|
153
|
+
|
|
154
|
+
let chooseOAuthCredentialsForSetup = async (opts: {
|
|
155
|
+
store: Awaited<ReturnType<typeof createClientContext>>['store'];
|
|
156
|
+
authMethod: { id: string; name: string; type: string };
|
|
157
|
+
clientId?: string;
|
|
158
|
+
clientSecret?: string;
|
|
159
|
+
oauthCredential?: string;
|
|
160
|
+
}) => {
|
|
161
|
+
if (opts.authMethod.type !== 'auth.oauth') {
|
|
162
|
+
return null;
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
if (opts.clientId || opts.clientSecret) {
|
|
166
|
+
let credential = await createOAuthCredentialInteractive({
|
|
167
|
+
store: opts.store,
|
|
168
|
+
authMethod: opts.authMethod,
|
|
169
|
+
clientId: opts.clientId,
|
|
170
|
+
clientSecret: opts.clientSecret
|
|
171
|
+
});
|
|
172
|
+
return {
|
|
173
|
+
credential,
|
|
174
|
+
clientId: credential.clientId,
|
|
175
|
+
clientSecret: credential.clientSecret
|
|
176
|
+
};
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
if (opts.oauthCredential) {
|
|
180
|
+
let credential = opts.store.getOAuthCredential(opts.oauthCredential, opts.authMethod.id);
|
|
181
|
+
if (!credential) {
|
|
182
|
+
throw new Error(`Unknown OAuth credentials: ${opts.oauthCredential}`);
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
return {
|
|
186
|
+
credential,
|
|
187
|
+
clientId: credential.clientId,
|
|
188
|
+
clientSecret: credential.clientSecret
|
|
189
|
+
};
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
let credentials = opts.store.listOAuthCredentials(opts.authMethod.id);
|
|
193
|
+
if (credentials.length === 0) {
|
|
194
|
+
let credential = await createOAuthCredentialInteractive({
|
|
195
|
+
store: opts.store,
|
|
196
|
+
authMethod: opts.authMethod
|
|
197
|
+
});
|
|
198
|
+
return {
|
|
199
|
+
credential,
|
|
200
|
+
clientId: credential.clientId,
|
|
201
|
+
clientSecret: credential.clientSecret
|
|
202
|
+
};
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
if (credentials.length === 1) {
|
|
206
|
+
let useExisting = await confirm({
|
|
207
|
+
message: `Use saved OAuth credentials "${credentials[0]!.name}"?`,
|
|
208
|
+
default: true
|
|
209
|
+
});
|
|
210
|
+
|
|
211
|
+
if (useExisting) {
|
|
212
|
+
let credential = credentials[0]!;
|
|
213
|
+
return {
|
|
214
|
+
credential,
|
|
215
|
+
clientId: credential.clientId,
|
|
216
|
+
clientSecret: credential.clientSecret
|
|
217
|
+
};
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
let credential = await createOAuthCredentialInteractive({
|
|
221
|
+
store: opts.store,
|
|
222
|
+
authMethod: opts.authMethod
|
|
223
|
+
});
|
|
224
|
+
return {
|
|
225
|
+
credential,
|
|
226
|
+
clientId: credential.clientId,
|
|
227
|
+
clientSecret: credential.clientSecret
|
|
228
|
+
};
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
let selected = await select({
|
|
232
|
+
message: 'Choose OAuth credentials',
|
|
233
|
+
choices: [
|
|
234
|
+
...credentials.map(credential => ({
|
|
235
|
+
name: `${credential.name} (${credential.clientId})`,
|
|
236
|
+
value: credential.id
|
|
237
|
+
})),
|
|
238
|
+
{
|
|
239
|
+
name: 'Create new OAuth credentials',
|
|
240
|
+
value: '__new__'
|
|
241
|
+
}
|
|
242
|
+
]
|
|
243
|
+
});
|
|
244
|
+
|
|
245
|
+
if (selected === '__new__') {
|
|
246
|
+
let credential = await createOAuthCredentialInteractive({
|
|
247
|
+
store: opts.store,
|
|
248
|
+
authMethod: opts.authMethod
|
|
249
|
+
});
|
|
250
|
+
return {
|
|
251
|
+
credential,
|
|
252
|
+
clientId: credential.clientId,
|
|
253
|
+
clientSecret: credential.clientSecret
|
|
254
|
+
};
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
let credential = opts.store.getOAuthCredential(selected, opts.authMethod.id);
|
|
258
|
+
if (!credential) {
|
|
259
|
+
throw new Error(`Unknown OAuth credentials: ${selected}`);
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
return {
|
|
263
|
+
credential,
|
|
264
|
+
clientId: credential.clientId,
|
|
265
|
+
clientSecret: credential.clientSecret
|
|
266
|
+
};
|
|
267
|
+
};
|
|
268
|
+
|
|
269
|
+
let runAuthSetup = async (opts: AuthSetupOptions): Promise<SlatesStoredAuth> => {
|
|
270
|
+
let { store, profile, client } = await createClientContext({
|
|
271
|
+
...opts,
|
|
272
|
+
autoRefresh: false
|
|
273
|
+
});
|
|
274
|
+
client.clearAuth();
|
|
275
|
+
let authMethod = await chooseAuthMethod({
|
|
276
|
+
client,
|
|
277
|
+
authMethodId: opts.authMethodId,
|
|
278
|
+
forcePrompt: !opts.authMethodId
|
|
279
|
+
});
|
|
280
|
+
|
|
281
|
+
let defaultInput = authMethod.capabilities.getDefaultInput?.enabled
|
|
282
|
+
? ((await client.getDefaultAuthInput(authMethod.id)).input ?? {})
|
|
283
|
+
: {};
|
|
284
|
+
let authInput =
|
|
285
|
+
parseJsonObject(opts.input, 'auth input') ??
|
|
286
|
+
(await promptForObjectSchema(authMethod.inputSchema, defaultInput));
|
|
287
|
+
|
|
288
|
+
if (authMethod.capabilities.handleChangedInput?.enabled) {
|
|
289
|
+
authInput =
|
|
290
|
+
(
|
|
291
|
+
await client.updateAuthInput({
|
|
292
|
+
authenticationMethodId: authMethod.id,
|
|
293
|
+
previousInput: null,
|
|
294
|
+
newInput: authInput
|
|
295
|
+
})
|
|
296
|
+
).input ?? authInput;
|
|
297
|
+
}
|
|
298
|
+
|
|
299
|
+
let output: JsonObject;
|
|
300
|
+
let finalInput = authInput;
|
|
301
|
+
let callbackState: JsonObject | null = null;
|
|
302
|
+
let scopes = parseList(opts.scopes);
|
|
303
|
+
|
|
304
|
+
if (authMethod.type === 'auth.oauth') {
|
|
305
|
+
let callback = await createOAuthCallbackListener();
|
|
306
|
+
let redirectUri = normalizeCallbackRedirectUriForIntegration(
|
|
307
|
+
opts.integration,
|
|
308
|
+
callback.redirectUri,
|
|
309
|
+
authMethod.id
|
|
310
|
+
);
|
|
311
|
+
console.log(`OAuth redirect URL: ${redirectUri}`);
|
|
312
|
+
|
|
313
|
+
let resolvedOAuthCredentials = await chooseOAuthCredentialsForSetup({
|
|
314
|
+
store,
|
|
315
|
+
authMethod,
|
|
316
|
+
clientId: opts.clientId,
|
|
317
|
+
clientSecret: opts.clientSecret,
|
|
318
|
+
oauthCredential: opts.oauthCredential
|
|
319
|
+
});
|
|
320
|
+
if (!resolvedOAuthCredentials) {
|
|
321
|
+
throw new Error(`Authentication method ${authMethod.id} is not OAuth.`);
|
|
322
|
+
}
|
|
323
|
+
|
|
324
|
+
let clientId = resolvedOAuthCredentials.clientId;
|
|
325
|
+
let clientSecret = resolvedOAuthCredentials.clientSecret;
|
|
326
|
+
scopes = await chooseScopes(authMethod, scopes);
|
|
327
|
+
|
|
328
|
+
let authorizationUrl = await client.getAuthorizationUrl({
|
|
329
|
+
authenticationMethodId: authMethod.id,
|
|
330
|
+
redirectUri,
|
|
331
|
+
state: callback.state,
|
|
332
|
+
input: authInput,
|
|
333
|
+
clientId,
|
|
334
|
+
clientSecret,
|
|
335
|
+
scopes
|
|
336
|
+
});
|
|
337
|
+
|
|
338
|
+
callbackState = authorizationUrl.callbackState ?? null;
|
|
339
|
+
finalInput = authorizationUrl.input ?? authInput;
|
|
340
|
+
|
|
341
|
+
printBrowserUrl(authorizationUrl.authorizationUrl);
|
|
342
|
+
let callbackResult = await callback.wait();
|
|
343
|
+
if (callbackResult.state !== callback.state) {
|
|
344
|
+
throw new Error('OAuth state mismatch.');
|
|
345
|
+
}
|
|
346
|
+
|
|
347
|
+
let authOutput = await client.handleAuthorizationCallback({
|
|
348
|
+
authenticationMethodId: authMethod.id,
|
|
349
|
+
code: callbackResult.code,
|
|
350
|
+
state: callbackResult.state,
|
|
351
|
+
redirectUri,
|
|
352
|
+
input: finalInput,
|
|
353
|
+
clientId,
|
|
354
|
+
clientSecret,
|
|
355
|
+
scopes,
|
|
356
|
+
callbackParams: callbackResult.callbackParams,
|
|
357
|
+
callbackState: callbackState ?? undefined
|
|
358
|
+
});
|
|
359
|
+
|
|
360
|
+
output = authOutput.output;
|
|
361
|
+
finalInput = authOutput.input ?? finalInput;
|
|
362
|
+
scopes = authOutput.scopes ?? scopes;
|
|
363
|
+
|
|
364
|
+
let profileInfo = authMethod.capabilities.getProfile?.enabled
|
|
365
|
+
? await client.getAuthProfile({
|
|
366
|
+
authenticationMethodId: authMethod.id,
|
|
367
|
+
output,
|
|
368
|
+
input: finalInput,
|
|
369
|
+
scopes
|
|
370
|
+
})
|
|
371
|
+
: null;
|
|
372
|
+
|
|
373
|
+
let stored = store.upsertAuth(profile.id, {
|
|
374
|
+
authMethodId: authMethod.id,
|
|
375
|
+
authMethodName: authMethod.name,
|
|
376
|
+
authType: authMethod.type,
|
|
377
|
+
input: finalInput,
|
|
378
|
+
output,
|
|
379
|
+
oauthCredentialId: resolvedOAuthCredentials.credential?.id,
|
|
380
|
+
scopes,
|
|
381
|
+
clientId,
|
|
382
|
+
clientSecret,
|
|
383
|
+
callbackState,
|
|
384
|
+
profile: profileInfo?.profile ?? null
|
|
385
|
+
});
|
|
386
|
+
|
|
387
|
+
await store.save();
|
|
388
|
+
return stored;
|
|
389
|
+
}
|
|
390
|
+
|
|
391
|
+
output = (
|
|
392
|
+
await client.getAuthOutput({
|
|
393
|
+
authenticationMethodId: authMethod.id,
|
|
394
|
+
input: authInput
|
|
395
|
+
})
|
|
396
|
+
).output;
|
|
397
|
+
|
|
398
|
+
let profileInfo = authMethod.capabilities.getProfile?.enabled
|
|
399
|
+
? await client.getAuthProfile({
|
|
400
|
+
authenticationMethodId: authMethod.id,
|
|
401
|
+
output,
|
|
402
|
+
input: finalInput,
|
|
403
|
+
scopes
|
|
404
|
+
})
|
|
405
|
+
: null;
|
|
406
|
+
|
|
407
|
+
let stored = store.upsertAuth(profile.id, {
|
|
408
|
+
authMethodId: authMethod.id,
|
|
409
|
+
authMethodName: authMethod.name,
|
|
410
|
+
authType: authMethod.type,
|
|
411
|
+
input: finalInput,
|
|
412
|
+
output,
|
|
413
|
+
scopes,
|
|
414
|
+
profile: profileInfo?.profile ?? null
|
|
415
|
+
});
|
|
416
|
+
|
|
417
|
+
await store.save();
|
|
418
|
+
return stored;
|
|
419
|
+
};
|
|
420
|
+
|
|
421
|
+
export let setupAuth = async (opts: AuthSetupOptions) => runAuthSetup(opts);
|
|
422
|
+
|
|
423
|
+
export let refreshAuth = async (opts: WithProfile & { authMethodId?: string }) => {
|
|
424
|
+
let { store, profile, client } = await createClientContext(opts);
|
|
425
|
+
let storedAuth = store.getAuth(profile.id, opts.authMethodId);
|
|
426
|
+
if (!storedAuth) {
|
|
427
|
+
throw new Error('No stored authentication was found for this profile.');
|
|
428
|
+
}
|
|
429
|
+
|
|
430
|
+
let authMethod = await client.getAuthMethod(storedAuth.authMethodId);
|
|
431
|
+
if (!authMethod.authenticationMethod.capabilities.handleTokenRefresh?.enabled) {
|
|
432
|
+
throw new Error('This authentication method does not support token refresh.');
|
|
433
|
+
}
|
|
434
|
+
|
|
435
|
+
let refreshed = await client.refreshToken({
|
|
436
|
+
authenticationMethodId: storedAuth.authMethodId,
|
|
437
|
+
output: storedAuth.output,
|
|
438
|
+
input: storedAuth.input,
|
|
439
|
+
clientId: storedAuth.clientId ?? '',
|
|
440
|
+
clientSecret: storedAuth.clientSecret ?? '',
|
|
441
|
+
scopes: storedAuth.scopes
|
|
442
|
+
});
|
|
443
|
+
|
|
444
|
+
let updated = store.upsertAuth(profile.id, {
|
|
445
|
+
...storedAuth,
|
|
446
|
+
input: refreshed.input ?? storedAuth.input,
|
|
447
|
+
output: refreshed.output
|
|
448
|
+
});
|
|
449
|
+
await store.save();
|
|
450
|
+
return updated;
|
|
451
|
+
};
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import { createClientContext } from '../lib/context';
|
|
2
|
+
import { parseJsonObject, promptForObjectSchema } from '../lib/prompts';
|
|
3
|
+
import type { JsonInput, WithProfile } from '../lib/types';
|
|
4
|
+
|
|
5
|
+
export let getConfig = async (opts: WithProfile) => {
|
|
6
|
+
let { profile } = await createClientContext(opts);
|
|
7
|
+
return profile.config;
|
|
8
|
+
};
|
|
9
|
+
|
|
10
|
+
export let getConfigSchema = async (opts: WithProfile) => {
|
|
11
|
+
let { client } = await createClientContext(opts);
|
|
12
|
+
return client.getConfigSchema();
|
|
13
|
+
};
|
|
14
|
+
|
|
15
|
+
export let setConfig = async (opts: WithProfile & JsonInput) => {
|
|
16
|
+
let { store, profile, client } = await createClientContext(opts);
|
|
17
|
+
let previousConfig = profile.config;
|
|
18
|
+
let schema = (await client.getConfigSchema()).schema;
|
|
19
|
+
let defaultConfig = (await client.getDefaultConfig()).config ?? {};
|
|
20
|
+
let desiredConfig =
|
|
21
|
+
parseJsonObject(opts.input, 'config input') ??
|
|
22
|
+
(await promptForObjectSchema(schema, previousConfig ?? defaultConfig));
|
|
23
|
+
let result = await client.updateConfig(previousConfig, desiredConfig);
|
|
24
|
+
let finalConfig = result.config ?? desiredConfig;
|
|
25
|
+
store.setProfileConfig(profile.id, finalConfig);
|
|
26
|
+
await store.save();
|
|
27
|
+
return {
|
|
28
|
+
...result,
|
|
29
|
+
config: finalConfig
|
|
30
|
+
};
|
|
31
|
+
};
|
|
@@ -0,0 +1,151 @@
|
|
|
1
|
+
import { input } from '@inquirer/prompts';
|
|
2
|
+
import { createSlatesClientFromProfile, type openSlatesCliStore } from '@slates/profiles';
|
|
3
|
+
import path from 'path';
|
|
4
|
+
import { chooseProfile, openIntegrationStore, syncProfileMetadata } from '../lib/context';
|
|
5
|
+
import type { WithProfile } from '../lib/types';
|
|
6
|
+
|
|
7
|
+
let normalizeEntry = (rootDir: string, entry: string) => {
|
|
8
|
+
let absolute = path.isAbsolute(entry) ? entry : path.resolve(process.cwd(), entry);
|
|
9
|
+
let relative = path.relative(rootDir, absolute);
|
|
10
|
+
return relative && !relative.startsWith('..') && !path.isAbsolute(relative)
|
|
11
|
+
? relative
|
|
12
|
+
: absolute;
|
|
13
|
+
};
|
|
14
|
+
|
|
15
|
+
let getNextSetupProfileName = async (
|
|
16
|
+
store: Awaited<ReturnType<typeof openSlatesCliStore>>
|
|
17
|
+
) => {
|
|
18
|
+
let names = new Set(store.listProfiles().map(profile => profile.name));
|
|
19
|
+
if (!names.has('default')) {
|
|
20
|
+
return 'default';
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
let suffix = 2;
|
|
24
|
+
while (names.has(`default-${suffix}`)) {
|
|
25
|
+
suffix += 1;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
return `default-${suffix}`;
|
|
29
|
+
};
|
|
30
|
+
|
|
31
|
+
let createProfile = async (
|
|
32
|
+
opts: WithProfile & {
|
|
33
|
+
name?: string;
|
|
34
|
+
entry?: string;
|
|
35
|
+
exportName?: string;
|
|
36
|
+
useAsDefault?: boolean;
|
|
37
|
+
initializeConfig?: boolean;
|
|
38
|
+
interactive?: boolean;
|
|
39
|
+
}
|
|
40
|
+
) => {
|
|
41
|
+
let { integration, store } = await openIntegrationStore(opts.integration);
|
|
42
|
+
let interactive = opts.interactive ?? true;
|
|
43
|
+
|
|
44
|
+
let defaultName =
|
|
45
|
+
opts.name ??
|
|
46
|
+
(opts.initializeConfig
|
|
47
|
+
? await getNextSetupProfileName(store)
|
|
48
|
+
: `profile-${store.listProfiles().length + 1}`);
|
|
49
|
+
let name =
|
|
50
|
+
opts.name ??
|
|
51
|
+
(interactive
|
|
52
|
+
? await input({ message: 'Profile name', default: defaultName })
|
|
53
|
+
: defaultName);
|
|
54
|
+
let defaultEntry = opts.entry ?? integration.entry;
|
|
55
|
+
let entry =
|
|
56
|
+
opts.entry ??
|
|
57
|
+
(interactive
|
|
58
|
+
? await input({
|
|
59
|
+
message: 'Local slate entry file',
|
|
60
|
+
default: defaultEntry
|
|
61
|
+
})
|
|
62
|
+
: defaultEntry);
|
|
63
|
+
let exportName =
|
|
64
|
+
opts.exportName ??
|
|
65
|
+
(interactive
|
|
66
|
+
? await input({ message: 'Export name (optional)', default: 'provider' })
|
|
67
|
+
: 'provider');
|
|
68
|
+
|
|
69
|
+
let profile = store.upsertProfile({
|
|
70
|
+
name,
|
|
71
|
+
target: {
|
|
72
|
+
type: 'local',
|
|
73
|
+
entry: normalizeEntry(store.rootDir, entry),
|
|
74
|
+
exportName: exportName.trim() ? exportName.trim() : undefined
|
|
75
|
+
}
|
|
76
|
+
});
|
|
77
|
+
|
|
78
|
+
let client = await createSlatesClientFromProfile(profile);
|
|
79
|
+
await syncProfileMetadata({ store, profile, client });
|
|
80
|
+
|
|
81
|
+
if (opts.initializeConfig) {
|
|
82
|
+
let defaultConfig = (await client.getDefaultConfig()).config ?? {};
|
|
83
|
+
let result = await client.updateConfig(null, defaultConfig);
|
|
84
|
+
store.setProfileConfig(profile.id, result.config ?? defaultConfig);
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
if (opts.useAsDefault ?? store.listProfiles().length === 1) {
|
|
88
|
+
store.setCurrentProfile(profile.id);
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
await store.save();
|
|
92
|
+
|
|
93
|
+
return profile;
|
|
94
|
+
};
|
|
95
|
+
|
|
96
|
+
export let addProfile = async (
|
|
97
|
+
opts: WithProfile & {
|
|
98
|
+
name?: string;
|
|
99
|
+
entry?: string;
|
|
100
|
+
exportName?: string;
|
|
101
|
+
useAsDefault?: boolean;
|
|
102
|
+
}
|
|
103
|
+
) =>
|
|
104
|
+
createProfile({
|
|
105
|
+
...opts,
|
|
106
|
+
interactive: true
|
|
107
|
+
});
|
|
108
|
+
|
|
109
|
+
export let setupIntegration = async (
|
|
110
|
+
opts: WithProfile & {
|
|
111
|
+
name?: string;
|
|
112
|
+
exportName?: string;
|
|
113
|
+
}
|
|
114
|
+
) =>
|
|
115
|
+
createProfile({
|
|
116
|
+
...opts,
|
|
117
|
+
useAsDefault: true,
|
|
118
|
+
initializeConfig: true,
|
|
119
|
+
interactive: false
|
|
120
|
+
});
|
|
121
|
+
|
|
122
|
+
export let listProfiles = async (opts: Pick<WithProfile, 'integration'>) => {
|
|
123
|
+
let { store } = await openIntegrationStore(opts.integration);
|
|
124
|
+
let current = store.getProfile();
|
|
125
|
+
return store.listProfiles().map(profile => ({
|
|
126
|
+
name: profile.name,
|
|
127
|
+
id: profile.id,
|
|
128
|
+
current: profile.id === current?.id,
|
|
129
|
+
entry: profile.target.type === 'local' ? profile.target.entry : null,
|
|
130
|
+
authMethods: Object.keys(profile.auth)
|
|
131
|
+
}));
|
|
132
|
+
};
|
|
133
|
+
|
|
134
|
+
export let getProfile = async (opts: WithProfile) => {
|
|
135
|
+
let { store } = await openIntegrationStore(opts.integration);
|
|
136
|
+
return store.requireProfile(opts.profile);
|
|
137
|
+
};
|
|
138
|
+
|
|
139
|
+
export let useProfile = async (opts: WithProfile) => {
|
|
140
|
+
let { store, profile } = await chooseProfile(opts);
|
|
141
|
+
store.setCurrentProfile(profile.id);
|
|
142
|
+
await store.save();
|
|
143
|
+
return profile;
|
|
144
|
+
};
|
|
145
|
+
|
|
146
|
+
export let removeProfile = async (opts: WithProfile) => {
|
|
147
|
+
let { store, profile } = await chooseProfile(opts);
|
|
148
|
+
store.removeProfile(profile.id);
|
|
149
|
+
await store.save();
|
|
150
|
+
return profile;
|
|
151
|
+
};
|