@mintmcp/hosted-cli 0.0.15 → 0.0.17

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/auth.js ADDED
@@ -0,0 +1,659 @@
1
+ import keytar from "keytar";
2
+ import { z } from "zod";
3
+ export const EnvSchema = z.enum(["staging", "production"]);
4
+ const CLIENT_IDS = {
5
+ staging: "client_01K0WB8ABW72JSBZ62V98AZMPS",
6
+ production: "client_01K0WB8AHKS2J39SFKAPTHTR90",
7
+ };
8
+ const LegacyAuthSchema = z.object({
9
+ accessToken: z.string(),
10
+ email: z.string(),
11
+ organizationId: z.string(),
12
+ });
13
+ const WorkOsUserSchema = z.object({
14
+ id: z.string(),
15
+ email: z.string(),
16
+ first_name: z.string().nullable().optional(),
17
+ last_name: z.string().nullable().optional(),
18
+ });
19
+ const TokenResponseSchema = z.object({
20
+ access_token: z.string(),
21
+ refresh_token: z.string().optional(),
22
+ organization_id: z.string(),
23
+ expires_in: z.number().int().positive().optional(),
24
+ user: WorkOsUserSchema,
25
+ });
26
+ const StoredSessionSchema = z.object({
27
+ userId: z.string(),
28
+ email: z.string(),
29
+ firstName: z.string().nullable().optional(),
30
+ lastName: z.string().nullable().optional(),
31
+ refreshToken: z.string().optional(),
32
+ currentOrganizationId: z.string().optional(),
33
+ lastUsedAt: z.string().optional(),
34
+ });
35
+ const StoredOrgAccessSchema = z.object({
36
+ userId: z.string(),
37
+ organizationId: z.string(),
38
+ accessToken: z.string(),
39
+ expiresAt: z.string().optional(),
40
+ lastUsedAt: z.string().optional(),
41
+ });
42
+ const StoredSessionAccountSchema = z.object({
43
+ env: EnvSchema,
44
+ userId: z.string(),
45
+ });
46
+ const StoredAuthAccountSchema = z.object({
47
+ env: EnvSchema,
48
+ userId: z.string(),
49
+ organizationId: z.string(),
50
+ });
51
+ const keychainServiceName = "mintmcp-credentials";
52
+ const currentAccountPrefix = "current:";
53
+ const sessionAccountPrefix = "session:";
54
+ const accessAccountPrefix = "access:";
55
+ const deviceCodeGrantType = "urn:ietf:params:oauth:grant-type:device_code";
56
+ const refreshGrantType = "refresh_token";
57
+ const workosDeviceAuthorizeUrl = "https://api.workos.com/user_management/authorize/device";
58
+ const workosAuthenticateUrl = "https://api.workos.com/user_management/authenticate";
59
+ function currentSelectionStorageKey(env) {
60
+ return `${currentAccountPrefix}${env}`;
61
+ }
62
+ function makeStoredSessionAccount(env, userId) {
63
+ return { env, userId };
64
+ }
65
+ function serializeStoredSessionAccount(account) {
66
+ return `${sessionAccountPrefix}${account.env}:${account.userId}`;
67
+ }
68
+ function parseStoredSessionAccount(value) {
69
+ const match = new RegExp(`^${sessionAccountPrefix}(staging|production):(.+)$`).exec(value);
70
+ if (!match) {
71
+ return null;
72
+ }
73
+ try {
74
+ return StoredSessionAccountSchema.parse({
75
+ env: match[1],
76
+ userId: match[2],
77
+ });
78
+ }
79
+ catch {
80
+ return null;
81
+ }
82
+ }
83
+ function makeStoredAuthAccount(env, userId, organizationId) {
84
+ return { env, userId, organizationId };
85
+ }
86
+ function serializeStoredAuthAccount(account) {
87
+ return `${accessAccountPrefix}${account.env}:${account.userId}:${account.organizationId}`;
88
+ }
89
+ function parseStoredAuthAccount(value) {
90
+ const match = new RegExp(`^${accessAccountPrefix}(staging|production):([^:]+):(.+)$`).exec(value);
91
+ if (!match) {
92
+ return null;
93
+ }
94
+ try {
95
+ return StoredAuthAccountSchema.parse({
96
+ env: match[1],
97
+ userId: match[2],
98
+ organizationId: match[3],
99
+ });
100
+ }
101
+ catch {
102
+ return null;
103
+ }
104
+ }
105
+ export function sameStoredAuthAccount(left, right) {
106
+ return (left.env === right.env &&
107
+ left.userId === right.userId &&
108
+ left.organizationId === right.organizationId);
109
+ }
110
+ export function getTokenClaims(accessToken) {
111
+ const [, payload] = accessToken.split(".");
112
+ if (!payload) {
113
+ return {
114
+ exp: undefined,
115
+ org_id: undefined,
116
+ sid: undefined,
117
+ sub: undefined,
118
+ };
119
+ }
120
+ try {
121
+ const parsed = JSON.parse(Buffer.from(payload, "base64url").toString());
122
+ return {
123
+ exp: typeof parsed.exp === "number" ? parsed.exp : undefined,
124
+ org_id: typeof parsed.org_id === "string" ? parsed.org_id : undefined,
125
+ sid: typeof parsed.sid === "string" ? parsed.sid : undefined,
126
+ sub: typeof parsed.sub === "string" ? parsed.sub : undefined,
127
+ };
128
+ }
129
+ catch {
130
+ return {
131
+ exp: undefined,
132
+ org_id: undefined,
133
+ sid: undefined,
134
+ sub: undefined,
135
+ };
136
+ }
137
+ }
138
+ function getExpiresAt(accessToken, expiresInSeconds) {
139
+ if (expiresInSeconds != undefined) {
140
+ return new Date(Date.now() + expiresInSeconds * 1000).toISOString();
141
+ }
142
+ const claims = getTokenClaims(accessToken);
143
+ if (claims.exp != undefined) {
144
+ return new Date(claims.exp * 1000).toISOString();
145
+ }
146
+ return undefined;
147
+ }
148
+ function isAccessTokenExpired(auth, skewMs = 60_000) {
149
+ if (auth.expiresAt == undefined) {
150
+ return false;
151
+ }
152
+ const expiresAtMs = Date.parse(auth.expiresAt);
153
+ if (Number.isNaN(expiresAtMs)) {
154
+ return false;
155
+ }
156
+ return expiresAtMs <= Date.now() + skewMs;
157
+ }
158
+ function makeAuthFromTokenResponse(tokenData, previousAuth) {
159
+ const parsed = TokenResponseSchema.parse(tokenData);
160
+ return {
161
+ userId: parsed.user.id,
162
+ email: parsed.user.email,
163
+ firstName: parsed.user.first_name ?? previousAuth?.firstName ?? null,
164
+ lastName: parsed.user.last_name ?? previousAuth?.lastName ?? null,
165
+ refreshToken: parsed.refresh_token ?? previousAuth?.refreshToken,
166
+ currentOrganizationId: parsed.organization_id,
167
+ organizationId: parsed.organization_id,
168
+ accessToken: parsed.access_token,
169
+ expiresAt: getExpiresAt(parsed.access_token, parsed.expires_in),
170
+ lastUsedAt: new Date().toISOString(),
171
+ };
172
+ }
173
+ function combineAuth(session, access) {
174
+ return {
175
+ userId: session.userId,
176
+ email: session.email,
177
+ firstName: session.firstName ?? null,
178
+ lastName: session.lastName ?? null,
179
+ refreshToken: session.refreshToken,
180
+ currentOrganizationId: session.currentOrganizationId,
181
+ lastUsedAt: access.lastUsedAt ?? session.lastUsedAt,
182
+ organizationId: access.organizationId,
183
+ accessToken: access.accessToken,
184
+ expiresAt: access.expiresAt,
185
+ };
186
+ }
187
+ export function formatAuthSummary(auth) {
188
+ const fullName = [auth.firstName, auth.lastName].filter(Boolean).join(" ");
189
+ const identity = fullName.length > 0 ? `${fullName} <${auth.email}>` : auth.email;
190
+ return `${identity} (${auth.userId}) in ${auth.organizationId}`;
191
+ }
192
+ async function clearLegacyAuth(env) {
193
+ await keytar.deletePassword(keychainServiceName, env);
194
+ }
195
+ async function getStoredSessionRecord(env, account) {
196
+ if (account.env !== env) {
197
+ return null;
198
+ }
199
+ const stored = await keytar.getPassword(keychainServiceName, serializeStoredSessionAccount(account));
200
+ if (!stored) {
201
+ return null;
202
+ }
203
+ try {
204
+ return {
205
+ account,
206
+ session: StoredSessionSchema.parse(JSON.parse(stored)),
207
+ };
208
+ }
209
+ catch (error) {
210
+ console.warn(`Invalid stored session for ${account.userId}, deleting:`, error);
211
+ await keytar.deletePassword(keychainServiceName, serializeStoredSessionAccount(account));
212
+ return null;
213
+ }
214
+ }
215
+ async function listStoredSessionRecords(env) {
216
+ const credentials = await keytar.findCredentials(keychainServiceName);
217
+ const records = [];
218
+ for (const credential of credentials) {
219
+ const account = parseStoredSessionAccount(credential.account);
220
+ if (!account || account.env !== env) {
221
+ continue;
222
+ }
223
+ try {
224
+ records.push({
225
+ account,
226
+ session: StoredSessionSchema.parse(JSON.parse(credential.password)),
227
+ });
228
+ }
229
+ catch (error) {
230
+ console.warn(`Invalid stored session for ${credential.account}, deleting:`, error);
231
+ await keytar.deletePassword(keychainServiceName, credential.account);
232
+ }
233
+ }
234
+ return records;
235
+ }
236
+ async function getStoredOrgAccessRecord(env, account) {
237
+ if (account.env !== env) {
238
+ return null;
239
+ }
240
+ const stored = await keytar.getPassword(keychainServiceName, serializeStoredAuthAccount(account));
241
+ if (!stored) {
242
+ return null;
243
+ }
244
+ try {
245
+ return StoredOrgAccessSchema.parse(JSON.parse(stored));
246
+ }
247
+ catch (error) {
248
+ console.warn(`Invalid stored org access for ${serializeStoredAuthAccount(account)}, deleting:`, error);
249
+ await keytar.deletePassword(keychainServiceName, serializeStoredAuthAccount(account));
250
+ return null;
251
+ }
252
+ }
253
+ async function getStoredAuthRecord(env, account) {
254
+ const access = await getStoredOrgAccessRecord(env, account);
255
+ if (!access) {
256
+ return null;
257
+ }
258
+ const sessionRecord = await getStoredSessionRecord(env, makeStoredSessionAccount(env, account.userId));
259
+ if (!sessionRecord) {
260
+ await keytar.deletePassword(keychainServiceName, serializeStoredAuthAccount(account));
261
+ return null;
262
+ }
263
+ return {
264
+ account,
265
+ session: sessionRecord.session,
266
+ access,
267
+ };
268
+ }
269
+ async function listStoredAuthRecords(env) {
270
+ const credentials = await keytar.findCredentials(keychainServiceName);
271
+ const records = [];
272
+ for (const credential of credentials) {
273
+ const account = parseStoredAuthAccount(credential.account);
274
+ if (!account || account.env !== env) {
275
+ continue;
276
+ }
277
+ const record = await getStoredAuthRecord(env, account);
278
+ if (record) {
279
+ records.push(record);
280
+ }
281
+ }
282
+ records.sort((left, right) => {
283
+ const leftTs = left.access.lastUsedAt != undefined
284
+ ? Date.parse(left.access.lastUsedAt)
285
+ : Number.NEGATIVE_INFINITY;
286
+ const rightTs = right.access.lastUsedAt != undefined
287
+ ? Date.parse(right.access.lastUsedAt)
288
+ : Number.NEGATIVE_INFINITY;
289
+ return rightTs - leftTs;
290
+ });
291
+ return records;
292
+ }
293
+ export async function listAuthRecords(env) {
294
+ return (await listStoredAuthRecords(env)).map((record) => ({
295
+ account: record.account,
296
+ auth: combineAuth(record.session, record.access),
297
+ }));
298
+ }
299
+ async function selectStoredAuthRecord(env, record) {
300
+ const now = new Date().toISOString();
301
+ const session = {
302
+ ...record.session,
303
+ currentOrganizationId: record.account.organizationId,
304
+ lastUsedAt: now,
305
+ };
306
+ const access = {
307
+ ...record.access,
308
+ lastUsedAt: now,
309
+ };
310
+ await keytar.setPassword(keychainServiceName, serializeStoredSessionAccount(makeStoredSessionAccount(env, session.userId)), JSON.stringify(session));
311
+ await keytar.setPassword(keychainServiceName, serializeStoredAuthAccount(record.account), JSON.stringify(access));
312
+ await keytar.setPassword(keychainServiceName, currentSelectionStorageKey(env), JSON.stringify(record.account));
313
+ return combineAuth(session, access);
314
+ }
315
+ async function saveStoredAuth(env, auth, options) {
316
+ const now = new Date().toISOString();
317
+ const account = makeStoredAuthAccount(env, auth.userId, auth.organizationId);
318
+ const session = {
319
+ userId: auth.userId,
320
+ email: auth.email,
321
+ firstName: auth.firstName ?? null,
322
+ lastName: auth.lastName ?? null,
323
+ refreshToken: auth.refreshToken,
324
+ currentOrganizationId: auth.organizationId,
325
+ lastUsedAt: now,
326
+ };
327
+ const access = {
328
+ userId: auth.userId,
329
+ organizationId: auth.organizationId,
330
+ accessToken: auth.accessToken,
331
+ expiresAt: auth.expiresAt,
332
+ lastUsedAt: now,
333
+ };
334
+ await keytar.setPassword(keychainServiceName, serializeStoredSessionAccount(makeStoredSessionAccount(env, auth.userId)), JSON.stringify(session));
335
+ await keytar.setPassword(keychainServiceName, serializeStoredAuthAccount(account), JSON.stringify(access));
336
+ const record = { account, session, access };
337
+ if (options?.selectCurrent !== false) {
338
+ await keytar.setPassword(keychainServiceName, currentSelectionStorageKey(env), JSON.stringify(account));
339
+ }
340
+ return record;
341
+ }
342
+ async function deleteStoredSessionAndAuthRecords(env, userId) {
343
+ const currentAccountValue = await keytar.getPassword(keychainServiceName, currentSelectionStorageKey(env));
344
+ const records = await listStoredAuthRecords(env);
345
+ for (const record of records) {
346
+ if (record.account.userId !== userId) {
347
+ continue;
348
+ }
349
+ await keytar.deletePassword(keychainServiceName, serializeStoredAuthAccount(record.account));
350
+ }
351
+ await keytar.deletePassword(keychainServiceName, serializeStoredSessionAccount(makeStoredSessionAccount(env, userId)));
352
+ if (currentAccountValue) {
353
+ try {
354
+ const currentAccount = StoredAuthAccountSchema.parse(JSON.parse(currentAccountValue));
355
+ if (currentAccount.userId === userId) {
356
+ await keytar.deletePassword(keychainServiceName, currentSelectionStorageKey(env));
357
+ }
358
+ }
359
+ catch {
360
+ await keytar.deletePassword(keychainServiceName, currentSelectionStorageKey(env));
361
+ }
362
+ }
363
+ }
364
+ async function maybeMigrateLegacyAuth(env) {
365
+ const stored = await keytar.getPassword(keychainServiceName, env);
366
+ if (!stored) {
367
+ return null;
368
+ }
369
+ let legacyAuth;
370
+ try {
371
+ legacyAuth = LegacyAuthSchema.parse(JSON.parse(stored));
372
+ }
373
+ catch (error) {
374
+ console.warn("Invalid legacy stored credentials, clearing:", error);
375
+ await clearLegacyAuth(env);
376
+ return null;
377
+ }
378
+ const claims = getTokenClaims(legacyAuth.accessToken);
379
+ if (!claims.sub) {
380
+ return null;
381
+ }
382
+ const migratedAuth = {
383
+ userId: claims.sub,
384
+ email: legacyAuth.email,
385
+ firstName: null,
386
+ lastName: null,
387
+ refreshToken: undefined,
388
+ currentOrganizationId: legacyAuth.organizationId,
389
+ lastUsedAt: new Date().toISOString(),
390
+ organizationId: legacyAuth.organizationId,
391
+ accessToken: legacyAuth.accessToken,
392
+ expiresAt: getExpiresAt(legacyAuth.accessToken),
393
+ };
394
+ const record = await saveStoredAuth(env, migratedAuth);
395
+ await clearLegacyAuth(env);
396
+ return combineAuth(record.session, record.access);
397
+ }
398
+ async function resolveSelectedAuth(env) {
399
+ const currentAccountValue = await keytar.getPassword(keychainServiceName, currentSelectionStorageKey(env));
400
+ if (currentAccountValue) {
401
+ try {
402
+ const currentAccount = StoredAuthAccountSchema.parse(JSON.parse(currentAccountValue));
403
+ const currentRecord = await getStoredAuthRecord(env, currentAccount);
404
+ if (currentRecord) {
405
+ return combineAuth(currentRecord.session, currentRecord.access);
406
+ }
407
+ }
408
+ catch {
409
+ // Fall through to clearing invalid current selection.
410
+ }
411
+ await keytar.deletePassword(keychainServiceName, currentSelectionStorageKey(env));
412
+ }
413
+ const migratedLegacyAuth = await maybeMigrateLegacyAuth(env);
414
+ if (migratedLegacyAuth) {
415
+ return migratedLegacyAuth;
416
+ }
417
+ const records = await listStoredAuthRecords(env);
418
+ const mostRecentRecord = records[0];
419
+ if (!mostRecentRecord) {
420
+ return null;
421
+ }
422
+ return await selectStoredAuthRecord(env, mostRecentRecord);
423
+ }
424
+ async function requestAuth(env) {
425
+ const clientId = CLIENT_IDS[env];
426
+ const deviceCodeResponse = await fetch(workosDeviceAuthorizeUrl, {
427
+ method: "POST",
428
+ headers: {
429
+ "Content-Type": "application/x-www-form-urlencoded",
430
+ },
431
+ body: new URLSearchParams({
432
+ client_id: clientId,
433
+ }),
434
+ });
435
+ if (!deviceCodeResponse.ok) {
436
+ throw new Error(`Failed to get device code: ${await deviceCodeResponse.text()}`);
437
+ }
438
+ const deviceData = (await deviceCodeResponse.json());
439
+ const { device_code: deviceCode, verification_uri_complete: verificationUriComplete, interval = 5, } = deviceData;
440
+ console.log(`To authenticate, visit ${verificationUriComplete}`);
441
+ const maxAttempts = 60;
442
+ for (let attempts = 0; attempts < maxAttempts; attempts += 1) {
443
+ await new Promise((resolve) => {
444
+ setTimeout(resolve, interval * 1000);
445
+ });
446
+ const tokenResponse = await fetch(workosAuthenticateUrl, {
447
+ method: "POST",
448
+ headers: {
449
+ "Content-Type": "application/x-www-form-urlencoded",
450
+ },
451
+ body: new URLSearchParams({
452
+ grant_type: deviceCodeGrantType,
453
+ device_code: deviceCode,
454
+ client_id: clientId,
455
+ }),
456
+ });
457
+ if (!tokenResponse.ok) {
458
+ continue;
459
+ }
460
+ const tokenData = (await tokenResponse.json());
461
+ if (tokenData.error) {
462
+ if (tokenData.error === "authorization_pending") {
463
+ continue;
464
+ }
465
+ else if (tokenData.error === "slow_down") {
466
+ await new Promise((resolve) => {
467
+ setTimeout(resolve, 5000);
468
+ });
469
+ continue;
470
+ }
471
+ else {
472
+ throw new Error(`Authentication failed: ${tokenData.error}`);
473
+ }
474
+ }
475
+ if (tokenData.access_token) {
476
+ return makeAuthFromTokenResponse(tokenData);
477
+ }
478
+ }
479
+ throw new Error("Authentication timeout");
480
+ }
481
+ async function refreshAuth(env, auth, organizationId = auth.organizationId) {
482
+ if (!auth.refreshToken) {
483
+ throw new Error("No refresh token is stored for the selected session.");
484
+ }
485
+ const response = await fetch(workosAuthenticateUrl, {
486
+ method: "POST",
487
+ headers: {
488
+ "Content-Type": "application/x-www-form-urlencoded",
489
+ },
490
+ body: new URLSearchParams({
491
+ grant_type: refreshGrantType,
492
+ refresh_token: auth.refreshToken,
493
+ client_id: CLIENT_IDS[env],
494
+ organization_id: organizationId,
495
+ }),
496
+ });
497
+ const rawBody = await response.text();
498
+ let body;
499
+ try {
500
+ body = JSON.parse(rawBody);
501
+ }
502
+ catch {
503
+ body = { error: rawBody };
504
+ }
505
+ if (!response.ok) {
506
+ const errorCode = typeof body.error === "string"
507
+ ? body.error
508
+ : rawBody;
509
+ throw new Error(`Refresh failed: ${errorCode}`);
510
+ }
511
+ if (typeof body.error === "string") {
512
+ throw new Error(`Refresh failed: ${body.error}`);
513
+ }
514
+ return makeAuthFromTokenResponse(body, auth);
515
+ }
516
+ export async function requestAndStoreAuth(env) {
517
+ const auth = await requestAuth(env);
518
+ const record = await saveStoredAuth(env, auth);
519
+ return combineAuth(record.session, record.access);
520
+ }
521
+ async function refreshOrRequestAuth(env, auth) {
522
+ if (auth.refreshToken) {
523
+ try {
524
+ const refreshedAuth = await refreshAuth(env, auth);
525
+ const record = await saveStoredAuth(env, refreshedAuth);
526
+ return combineAuth(record.session, record.access);
527
+ }
528
+ catch (error) {
529
+ const message = error instanceof Error ? error.message : String(error);
530
+ console.warn(`Stored session could not be refreshed, re-authenticating: ${message}`);
531
+ }
532
+ }
533
+ await deleteStoredSessionAndAuthRecords(env, auth.userId);
534
+ return await requestAndStoreAuth(env);
535
+ }
536
+ function matchesAuthFilters(auth, filters) {
537
+ if (filters.userId && auth.userId !== filters.userId) {
538
+ return false;
539
+ }
540
+ if (filters.email && auth.email !== filters.email) {
541
+ return false;
542
+ }
543
+ if (filters.organizationId &&
544
+ auth.organizationId !== filters.organizationId) {
545
+ return false;
546
+ }
547
+ return true;
548
+ }
549
+ function matchesSessionFilters(session, filters) {
550
+ if (filters.userId && session.userId !== filters.userId) {
551
+ return false;
552
+ }
553
+ if (filters.email && session.email !== filters.email) {
554
+ return false;
555
+ }
556
+ return true;
557
+ }
558
+ export async function clearAllAuth(env) {
559
+ const credentials = await keytar.findCredentials(keychainServiceName);
560
+ for (const credential of credentials) {
561
+ const isEnvScoped = parseStoredSessionAccount(credential.account)?.env === env ||
562
+ parseStoredAuthAccount(credential.account)?.env === env ||
563
+ credential.account === currentSelectionStorageKey(env) ||
564
+ credential.account === env;
565
+ if (isEnvScoped) {
566
+ await keytar.deletePassword(keychainServiceName, credential.account);
567
+ }
568
+ }
569
+ }
570
+ export async function clearSelectedAuth(env) {
571
+ const selectedAuth = await resolveSelectedAuth(env);
572
+ if (!selectedAuth) {
573
+ await keytar.deletePassword(keychainServiceName, currentSelectionStorageKey(env));
574
+ await clearLegacyAuth(env);
575
+ return null;
576
+ }
577
+ await deleteStoredSessionAndAuthRecords(env, selectedAuth.userId);
578
+ return selectedAuth;
579
+ }
580
+ export async function getCurrentAuthAccount(env) {
581
+ const currentAccountValue = await keytar.getPassword(keychainServiceName, currentSelectionStorageKey(env));
582
+ if (!currentAccountValue) {
583
+ return null;
584
+ }
585
+ try {
586
+ return StoredAuthAccountSchema.parse(JSON.parse(currentAccountValue));
587
+ }
588
+ catch {
589
+ return null;
590
+ }
591
+ }
592
+ export async function getAuth(env, options) {
593
+ const selectedAuth = await resolveSelectedAuth(env);
594
+ if (!selectedAuth) {
595
+ return await requestAndStoreAuth(env);
596
+ }
597
+ if (options?.forceRefresh || isAccessTokenExpired(selectedAuth)) {
598
+ return await refreshOrRequestAuth(env, selectedAuth);
599
+ }
600
+ return selectedAuth;
601
+ }
602
+ export async function getSelectedAuth(env) {
603
+ return await resolveSelectedAuth(env);
604
+ }
605
+ export async function useAuth(env, options) {
606
+ if (!options.userId && !options.email && !options.organizationId) {
607
+ throw new Error('Specify at least one filter, for example "hosted auth use --organization-id <id>".');
608
+ }
609
+ const records = await listStoredAuthRecords(env);
610
+ const matches = records.filter((record) => matchesAuthFilters(combineAuth(record.session, record.access), options));
611
+ if (matches.length > 1) {
612
+ throw new Error("Multiple cached credentials matched. Add --organization-id or --user-id to disambiguate.");
613
+ }
614
+ if (matches.length === 1) {
615
+ const match = matches[0];
616
+ if (!match) {
617
+ throw new Error(`No cached credentials matched for ${env}.`);
618
+ }
619
+ return {
620
+ auth: await selectStoredAuthRecord(env, match),
621
+ mode: "selected",
622
+ };
623
+ }
624
+ if (!options.organizationId) {
625
+ throw new Error(`No cached credentials matched for ${env}.`);
626
+ }
627
+ const selectedAuth = await resolveSelectedAuth(env);
628
+ const sessionCandidates = (await listStoredSessionRecords(env)).filter((record) => matchesSessionFilters(record.session, options));
629
+ let sourceAuth = null;
630
+ if (selectedAuth && matchesSessionFilters(selectedAuth, options)) {
631
+ sourceAuth = selectedAuth;
632
+ }
633
+ else if (sessionCandidates.length === 1) {
634
+ const sessionCandidate = sessionCandidates[0];
635
+ if (sessionCandidate) {
636
+ const sourceOrganizationId = sessionCandidate.session.currentOrganizationId ??
637
+ records.find((record) => record.account.userId === sessionCandidate.account.userId)?.account.organizationId;
638
+ if (sourceOrganizationId) {
639
+ const sourceRecord = await getStoredAuthRecord(env, makeStoredAuthAccount(env, sessionCandidate.account.userId, sourceOrganizationId));
640
+ if (sourceRecord) {
641
+ sourceAuth = combineAuth(sourceRecord.session, sourceRecord.access);
642
+ }
643
+ }
644
+ }
645
+ }
646
+ else if (sessionCandidates.length > 1) {
647
+ throw new Error("Multiple cached identities could be refreshed into that organization. Add --user-id or --email to choose one.");
648
+ }
649
+ if (!sourceAuth) {
650
+ throw new Error(`No cached credentials were available to switch into ${options.organizationId}. Run "hosted auth login" first.`);
651
+ }
652
+ const refreshedAuth = await refreshAuth(env, sourceAuth, options.organizationId);
653
+ const record = await saveStoredAuth(env, refreshedAuth);
654
+ return {
655
+ auth: combineAuth(record.session, record.access),
656
+ mode: "switched",
657
+ };
658
+ }
659
+ //# sourceMappingURL=auth.js.map