UNPKG

trigger.dev

Version:

A Command-Line Interface for Trigger.dev projects

152 lines • 6.37 kB
import { env } from "std-env"; import { CliApiClient } from "../apiClient.js"; import { CLOUD_API_URL } from "../consts.js"; import { readAuthConfigProfile, writeAuthConfigProfile } from "../utilities/configFiles.js"; import { NotAccessTokenError, validateAccessToken } from "../utilities/accessTokens.js"; import { getPersonalAccessToken } from "../commands/login.js"; import open from "open"; import pRetry from "p-retry"; import { ApiClient } from "@trigger.dev/core/v3"; export async function mcpAuth(options) { const opts = { defaultApiUrl: CLOUD_API_URL, ...options, }; const accessTokenFromEnv = env.TRIGGER_ACCESS_TOKEN; if (accessTokenFromEnv) { const validationResult = validateAccessToken(accessTokenFromEnv); if (!validationResult.success) { // We deliberately don't surface the existence of organization access tokens to the user for now, as they're only used internally. // Once we expose them in the application, we should also communicate that option here. throw new NotAccessTokenError("Your TRIGGER_ACCESS_TOKEN is not a Personal Access Token, they start with 'tr_pat_'. You can generate one here: https://cloud.trigger.dev/account/tokens"); } const auth = { accessToken: accessTokenFromEnv, apiUrl: env.TRIGGER_API_URL ?? opts.defaultApiUrl ?? CLOUD_API_URL, }; const apiClient = new CliApiClient(auth.apiUrl, auth.accessToken); const userData = await apiClient.whoAmI(); if (!userData.success) { throw new Error(userData.error); } return { ok: true, profile: options?.profile ?? "default", userId: userData.data.userId, email: userData.data.email, dashboardUrl: userData.data.dashboardUrl, auth: { accessToken: auth.accessToken, apiUrl: auth.apiUrl, tokenType: validationResult.type, }, }; } const authConfig = readAuthConfigProfile(options?.profile); if (authConfig && authConfig.accessToken) { const apiClient = new CliApiClient(authConfig.apiUrl ?? opts.defaultApiUrl, authConfig.accessToken); const userData = await apiClient.whoAmI(); if (!userData.success) { throw new Error(userData.error); } return { ok: true, profile: options?.profile ?? "default", userId: userData.data.userId, email: userData.data.email, dashboardUrl: userData.data.dashboardUrl, auth: { accessToken: authConfig.accessToken, apiUrl: authConfig.apiUrl ?? opts.defaultApiUrl, tokenType: "personal", }, }; } const apiClient = new CliApiClient(authConfig?.apiUrl ?? opts.defaultApiUrl); //generate authorization code const authorizationCodeResult = await createAuthorizationCode(apiClient); const url = new URL(authorizationCodeResult.url); url.searchParams.set("source", "mcp"); const clientName = options.server.server.getClientVersion()?.name; if (clientName) { url.searchParams.set("clientName", clientName); } // Only elicitInput if the client has the elicitation capability // Elicit the user to visit the authorization code URL const allowLogin = await askForLoginPermission(opts.server, url.toString()); if (!allowLogin) { return { ok: false, error: "User did not allow login", }; } // Open the authorization code URL in the browser await open(url.toString()); // Poll for the personal access token const indexResult = await pRetry(() => getPersonalAccessToken(apiClient, authorizationCodeResult.authorizationCode), { //this means we're polling, same distance between each attempt factor: 1, retries: 60, minTimeout: 1000, }); writeAuthConfigProfile({ accessToken: indexResult.token, apiUrl: opts.defaultApiUrl }, options?.profile); const client = new CliApiClient(opts.defaultApiUrl, indexResult.token); const userData = await client.whoAmI(); if (!userData.success) { throw new Error(userData.error); } return { ok: true, profile: options?.profile ?? "default", userId: userData.data.userId, email: userData.data.email, dashboardUrl: userData.data.dashboardUrl, auth: { accessToken: indexResult.token, apiUrl: opts.defaultApiUrl, tokenType: "personal", }, }; } async function createAuthorizationCode(apiClient) { const authorizationCodeResult = await apiClient.createAuthorizationCode(); if (!authorizationCodeResult.success) { throw new Error(`Failed to create authorization code\n${authorizationCodeResult.error}`); } return authorizationCodeResult.data; } async function askForLoginPermission(server, authorizationCodeUrl) { const capabilities = server.server.getClientCapabilities(); if (typeof capabilities?.elicitation !== "object") { return true; } const result = await server.server.elicitInput({ message: `You are not currently logged in. Would you like to login now? We'll automatically open the authorization code URL (${authorizationCodeUrl}) in your browser.`, requestedSchema: { type: "object", properties: { allowLogin: { type: "boolean", default: false, title: "Allow Login", description: "Whether to allow the user to login", }, }, required: ["allowLogin"], }, }); return result.action === "accept" && result.content?.allowLogin; } export async function createApiClientWithPublicJWT(auth, projectRef, envName, scopes, previewBranch) { const cliApiClient = new CliApiClient(auth.auth.apiUrl, auth.auth.accessToken, previewBranch); const jwt = await cliApiClient.getJWT(projectRef, envName, { claims: { scopes, }, }); if (!jwt.success) { return; } return new ApiClient(auth.auth.apiUrl, jwt.data.token); } //# sourceMappingURL=auth.js.map