import { Command } from "@commander-js/extra-typings"; import { chalkStderr } from "chalk"; import { oneoffContext } from "../bundler/context.js"; import { logFinishedStep, showSpinner } from "../bundler/log.js"; import { loadSelectedDeploymentCredentials } from "./lib/api.js"; import { actionDescription } from "./lib/command.js"; import { getDeploymentSelection } from "./lib/deploymentSelection.js"; import { CONVEX_DEPLOYMENT_TOKEN_ENV_VAR_NAME, CONVEX_DEPLOY_KEY_ENV_VAR_NAME, typedPlatformClient, } from "./lib/utils/utils.js"; export const deploymentTokenDelete = new Command("delete") .summary("Delete an access token") .description( "Delete an access token. Currently only deploy keys (deployment-scoped access tokens) are supported.\n\n" + "The positional `` can be the unique name of the deploy key (as passed to `token create`) or the deploy key value itself. The target deployment defaults to the currently-selected one; pass `--deployment` to target a different deployment.\n\n" + " Delete by name: `npx convex deployment token delete my-token`\n" + " Delete by value: `npx convex deployment token delete 'dev:happy-animal-123|ey...'`\n" + " Target prod: `npx convex deployment token delete ci-token --deployment prod`", ) .argument( "", "The unique name of the deploy key, or the deploy key value itself.", ) .allowExcessArguments(false) .addDeploymentSelectionOptions(actionDescription("Delete a deploy key for")) .showHelpAfterError() .action(async (nameOrToken, options) => { const ctx = await oneoffContext(options); const auth = ctx.bigBrainAuth(); if (auth === null || auth.kind !== "accessToken") { return await ctx.crash({ exitCode: 1, errorType: "fatal", printedMessage: `Deleting a deploy key requires being logged in with a personal access token. ${ auth === null ? "Run " : process.env[CONVEX_DEPLOYMENT_TOKEN_ENV_VAR_NAME] && !process.env[CONVEX_DEPLOY_KEY_ENV_VAR_NAME] ? `Unset ${CONVEX_DEPLOYMENT_TOKEN_ENV_VAR_NAME} and run ` : `Unset ${CONVEX_DEPLOY_KEY_ENV_VAR_NAME} and run ` }${chalkStderr.bold("npx convex login")} and try again.`, }); } const deploymentSelection = await getDeploymentSelection(ctx, options); const deployment = await loadSelectedDeploymentCredentials( ctx, deploymentSelection, { ensureLocalRunning: false }, ); if (deployment.deploymentFields === null) { return await ctx.crash({ exitCode: 1, errorType: "fatal", printedMessage: "Cannot delete a deploy key for a self-hosted deployment.", }); } const { deploymentName, deploymentType } = deployment.deploymentFields; if (deploymentType === "local" || deploymentType === "anonymous") { return await ctx.crash({ exitCode: 1, errorType: "fatal", printedMessage: `Cannot delete a deploy key for a ${deploymentType} deployment.`, }); } // A full deploy key has the form `:|`. If we // see the prefix without the `|`, the user almost certainly forgot to // quote: the shell ate `|` and everything after. if (/^(dev|prod|preview|local):[^|]*$/.test(nameOrToken)) { return await ctx.crash({ exitCode: 1, errorType: "fatal", printedMessage: `"${nameOrToken}" looks like a partial deploy key — your shell likely consumed the \`|\` and everything after it. Wrap the value in single quotes (e.g. ${chalkStderr.bold(`npx convex deployment token delete '${nameOrToken}|...'`)}) and try again.`, }); } // The server matches against just the token portion, so strip the prefix // when present so users can paste the value of CONVEX_DEPLOY_KEY directly. const pipeIdx = nameOrToken.indexOf("|"); const id = pipeIdx >= 0 ? nameOrToken.slice(pipeIdx + 1) : nameOrToken; showSpinner(`Deleting deploy key for ${deploymentName}...`); await typedPlatformClient(ctx).POST( "/deployments/{deployment_name}/delete_deploy_key", { params: { path: { deployment_name: deploymentName } }, body: { id }, }, ); logFinishedStep(`Deleted deploy key for ${deploymentName}.`); });