convex 1.47.0 → 1.48.0-alpha.0
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/browser.bundle.js +1 -1
- package/dist/browser.bundle.js.map +1 -1
- package/dist/cjs/bundler/context.js +7 -5
- package/dist/cjs/bundler/context.js.map +2 -2
- package/dist/cjs/bundler/fs.js +47 -6
- package/dist/cjs/bundler/fs.js.map +2 -2
- package/dist/cjs/cli/account.js +237 -0
- package/dist/cjs/cli/account.js.map +7 -0
- package/dist/cjs/cli/configure.js +15 -2
- package/dist/cjs/cli/configure.js.map +2 -2
- package/dist/cjs/cli/lib/deployKeyWarning.js +43 -0
- package/dist/cjs/cli/lib/deployKeyWarning.js.map +7 -0
- package/dist/cjs/cli/lib/deployment.js +2 -0
- package/dist/cjs/cli/lib/deployment.js.map +2 -2
- package/dist/cjs/cli/lib/deploymentSelection.js +27 -18
- package/dist/cjs/cli/lib/deploymentSelection.js.map +2 -2
- package/dist/cjs/cli/lib/envvars.js +32 -0
- package/dist/cjs/cli/lib/envvars.js.map +2 -2
- package/dist/cjs/cli/lib/fsUtils.js +1 -1
- package/dist/cjs/cli/lib/fsUtils.js.map +2 -2
- package/dist/cjs/cli/lib/generateDocs.js +1 -1
- package/dist/cjs/cli/lib/generateDocs.js.map +1 -1
- package/dist/cjs/cli/lib/login.js +28 -4
- package/dist/cjs/cli/lib/login.js.map +2 -2
- package/dist/cjs/cli/lib/mcp/requestContext.js +5 -1
- package/dist/cjs/cli/lib/mcp/requestContext.js.map +2 -2
- package/dist/cjs/cli/lib/profileManagement.js +196 -0
- package/dist/cjs/cli/lib/profileManagement.js.map +7 -0
- package/dist/cjs/cli/lib/profiles.js +333 -0
- package/dist/cjs/cli/lib/profiles.js.map +7 -0
- package/dist/cjs/cli/lib/utils/utils.js +18 -9
- package/dist/cjs/cli/lib/utils/utils.js.map +2 -2
- package/dist/cjs/cli/login.js +213 -114
- package/dist/cjs/cli/login.js.map +3 -3
- package/dist/cjs/cli/logout.js +113 -12
- package/dist/cjs/cli/logout.js.map +3 -3
- package/dist/cjs/cli/mcp.js +43 -10
- package/dist/cjs/cli/mcp.js.map +3 -3
- package/dist/cjs/cli/program.js +2 -1
- package/dist/cjs/cli/program.js.map +2 -2
- package/dist/cjs/index.js +1 -1
- package/dist/cjs/index.js.map +1 -1
- package/dist/cjs-types/bundler/context.d.ts +5 -0
- package/dist/cjs-types/bundler/context.d.ts.map +1 -1
- package/dist/cjs-types/bundler/fs.d.ts +7 -2
- package/dist/cjs-types/bundler/fs.d.ts.map +1 -1
- package/dist/cjs-types/cli/account.d.ts +6 -0
- package/dist/cjs-types/cli/account.d.ts.map +1 -0
- package/dist/cjs-types/cli/configure.d.ts.map +1 -1
- package/dist/cjs-types/cli/lib/deployKeyWarning.d.ts +3 -0
- package/dist/cjs-types/cli/lib/deployKeyWarning.d.ts.map +1 -0
- package/dist/cjs-types/cli/lib/deployment.d.ts +2 -0
- package/dist/cjs-types/cli/lib/deployment.d.ts.map +1 -1
- package/dist/cjs-types/cli/lib/deploymentSelection.d.ts +2 -1
- package/dist/cjs-types/cli/lib/deploymentSelection.d.ts.map +1 -1
- package/dist/cjs-types/cli/lib/envvars.d.ts +6 -0
- package/dist/cjs-types/cli/lib/envvars.d.ts.map +1 -1
- package/dist/cjs-types/cli/lib/fsUtils.d.ts +1 -0
- package/dist/cjs-types/cli/lib/fsUtils.d.ts.map +1 -1
- package/dist/cjs-types/cli/lib/login.d.ts +3 -1
- package/dist/cjs-types/cli/lib/login.d.ts.map +1 -1
- package/dist/cjs-types/cli/lib/mcp/requestContext.d.ts.map +1 -1
- package/dist/cjs-types/cli/lib/mcp/tools/insights.d.ts +2 -2
- package/dist/cjs-types/cli/lib/profileManagement.d.ts +10 -0
- package/dist/cjs-types/cli/lib/profileManagement.d.ts.map +1 -0
- package/dist/cjs-types/cli/lib/profiles.d.ts +59 -0
- package/dist/cjs-types/cli/lib/profiles.d.ts.map +1 -0
- package/dist/cjs-types/cli/lib/utils/utils.d.ts +5 -0
- package/dist/cjs-types/cli/lib/utils/utils.d.ts.map +1 -1
- package/dist/cjs-types/cli/login.d.ts +21 -0
- package/dist/cjs-types/cli/login.d.ts.map +1 -1
- package/dist/cjs-types/cli/logout.d.ts +8 -1
- package/dist/cjs-types/cli/logout.d.ts.map +1 -1
- package/dist/cjs-types/cli/mcp.d.ts.map +1 -1
- package/dist/cjs-types/cli/program.d.ts.map +1 -1
- package/dist/cjs-types/index.d.ts +1 -1
- package/dist/cjs-types/index.d.ts.map +1 -1
- package/dist/cli.bundle.cjs +2118 -1096
- package/dist/cli.bundle.cjs.map +4 -4
- package/dist/esm/bundler/context.js +7 -5
- package/dist/esm/bundler/context.js.map +2 -2
- package/dist/esm/bundler/fs.js +47 -6
- package/dist/esm/bundler/fs.js.map +2 -2
- package/dist/esm/cli/account.js +221 -0
- package/dist/esm/cli/account.js.map +7 -0
- package/dist/esm/cli/configure.js +15 -2
- package/dist/esm/cli/configure.js.map +2 -2
- package/dist/esm/cli/lib/deployKeyWarning.js +27 -0
- package/dist/esm/cli/lib/deployKeyWarning.js.map +7 -0
- package/dist/esm/cli/lib/deployment.js +2 -2
- package/dist/esm/cli/lib/deployment.js.map +2 -2
- package/dist/esm/cli/lib/deploymentSelection.js +35 -20
- package/dist/esm/cli/lib/deploymentSelection.js.map +2 -2
- package/dist/esm/cli/lib/envvars.js +35 -1
- package/dist/esm/cli/lib/envvars.js.map +2 -2
- package/dist/esm/cli/lib/fsUtils.js +1 -1
- package/dist/esm/cli/lib/fsUtils.js.map +2 -2
- package/dist/esm/cli/lib/generateDocs.js +1 -1
- package/dist/esm/cli/lib/generateDocs.js.map +1 -1
- package/dist/esm/cli/lib/login.js +36 -4
- package/dist/esm/cli/lib/login.js.map +2 -2
- package/dist/esm/cli/lib/mcp/requestContext.js +5 -1
- package/dist/esm/cli/lib/mcp/requestContext.js.map +2 -2
- package/dist/esm/cli/lib/profileManagement.js +179 -0
- package/dist/esm/cli/lib/profileManagement.js.map +7 -0
- package/dist/esm/cli/lib/profiles.js +288 -0
- package/dist/esm/cli/lib/profiles.js.map +7 -0
- package/dist/esm/cli/lib/utils/utils.js +16 -9
- package/dist/esm/cli/lib/utils/utils.js.map +2 -2
- package/dist/esm/cli/login.js +225 -112
- package/dist/esm/cli/login.js.map +3 -3
- package/dist/esm/cli/logout.js +110 -13
- package/dist/esm/cli/logout.js.map +2 -2
- package/dist/esm/cli/mcp.js +34 -10
- package/dist/esm/cli/mcp.js.map +2 -2
- package/dist/esm/cli/program.js +2 -1
- package/dist/esm/cli/program.js.map +2 -2
- package/dist/esm/index.js +1 -1
- package/dist/esm/index.js.map +1 -1
- package/dist/esm-types/bundler/context.d.ts +5 -0
- package/dist/esm-types/bundler/context.d.ts.map +1 -1
- package/dist/esm-types/bundler/fs.d.ts +7 -2
- package/dist/esm-types/bundler/fs.d.ts.map +1 -1
- package/dist/esm-types/cli/account.d.ts +6 -0
- package/dist/esm-types/cli/account.d.ts.map +1 -0
- package/dist/esm-types/cli/configure.d.ts.map +1 -1
- package/dist/esm-types/cli/lib/deployKeyWarning.d.ts +3 -0
- package/dist/esm-types/cli/lib/deployKeyWarning.d.ts.map +1 -0
- package/dist/esm-types/cli/lib/deployment.d.ts +2 -0
- package/dist/esm-types/cli/lib/deployment.d.ts.map +1 -1
- package/dist/esm-types/cli/lib/deploymentSelection.d.ts +2 -1
- package/dist/esm-types/cli/lib/deploymentSelection.d.ts.map +1 -1
- package/dist/esm-types/cli/lib/envvars.d.ts +6 -0
- package/dist/esm-types/cli/lib/envvars.d.ts.map +1 -1
- package/dist/esm-types/cli/lib/fsUtils.d.ts +1 -0
- package/dist/esm-types/cli/lib/fsUtils.d.ts.map +1 -1
- package/dist/esm-types/cli/lib/login.d.ts +3 -1
- package/dist/esm-types/cli/lib/login.d.ts.map +1 -1
- package/dist/esm-types/cli/lib/mcp/requestContext.d.ts.map +1 -1
- package/dist/esm-types/cli/lib/mcp/tools/insights.d.ts +2 -2
- package/dist/esm-types/cli/lib/profileManagement.d.ts +10 -0
- package/dist/esm-types/cli/lib/profileManagement.d.ts.map +1 -0
- package/dist/esm-types/cli/lib/profiles.d.ts +59 -0
- package/dist/esm-types/cli/lib/profiles.d.ts.map +1 -0
- package/dist/esm-types/cli/lib/utils/utils.d.ts +5 -0
- package/dist/esm-types/cli/lib/utils/utils.d.ts.map +1 -1
- package/dist/esm-types/cli/login.d.ts +21 -0
- package/dist/esm-types/cli/login.d.ts.map +1 -1
- package/dist/esm-types/cli/logout.d.ts +8 -1
- package/dist/esm-types/cli/logout.d.ts.map +1 -1
- package/dist/esm-types/cli/mcp.d.ts.map +1 -1
- package/dist/esm-types/cli/program.d.ts.map +1 -1
- package/dist/esm-types/index.d.ts +1 -1
- package/dist/esm-types/index.d.ts.map +1 -1
- package/dist/react.bundle.js +1 -1
- package/dist/react.bundle.js.map +1 -1
- package/package.json +1 -1
- package/src/bundler/context.ts +9 -5
- package/src/bundler/fs.ts +54 -5
- package/src/cli/account.ts +320 -0
- package/src/cli/configure.ts +18 -2
- package/src/cli/lib/deployKeyWarning.ts +43 -0
- package/src/cli/lib/deployment.ts +2 -2
- package/src/cli/lib/deploymentSelection.ts +37 -18
- package/src/cli/lib/envvars.ts +43 -1
- package/src/cli/lib/fsUtils.ts +5 -2
- package/src/cli/lib/generateDocs.ts +1 -1
- package/src/cli/lib/login.ts +47 -3
- package/src/cli/lib/mcp/requestContext.ts +7 -1
- package/src/cli/lib/profileManagement.ts +262 -0
- package/src/cli/lib/profiles.ts +398 -0
- package/src/cli/lib/utils/utils.ts +19 -15
- package/src/cli/login.ts +293 -168
- package/src/cli/logout.ts +142 -17
- package/src/cli/mcp.ts +48 -11
- package/src/cli/program.ts +2 -0
- package/src/index.ts +1 -1
package/dist/esm/cli/login.js
CHANGED
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
import { Command, Option } from "@commander-js/extra-typings";
|
|
3
|
-
import * as dotenv from "dotenv";
|
|
4
3
|
import { oneoffContext } from "../bundler/context.js";
|
|
5
4
|
import { logFinishedStep, logMessage, logWarning } from "../bundler/log.js";
|
|
6
5
|
import {
|
|
@@ -22,12 +21,7 @@ import {
|
|
|
22
21
|
teamDashboardUrl
|
|
23
22
|
} from "./lib/dashboard.js";
|
|
24
23
|
import { promptSearch, promptYesNo } from "./lib/utils/prompts.js";
|
|
25
|
-
import {
|
|
26
|
-
CONVEX_DEPLOY_KEY_ENV_VAR_NAME,
|
|
27
|
-
CONVEX_DEPLOYMENT_TOKEN_ENV_VAR_NAME,
|
|
28
|
-
ENV_VAR_FILE_PATH,
|
|
29
|
-
validateOrSelectTeam
|
|
30
|
-
} from "./lib/utils/utils.js";
|
|
24
|
+
import { validateOrSelectTeam } from "./lib/utils/utils.js";
|
|
31
25
|
import {
|
|
32
26
|
selectProject,
|
|
33
27
|
updateEnvAndConfigForDeploymentSelection
|
|
@@ -41,29 +35,30 @@ import {
|
|
|
41
35
|
removeAnonymousPrefix
|
|
42
36
|
} from "./lib/deployment.js";
|
|
43
37
|
import {
|
|
44
|
-
|
|
45
|
-
|
|
38
|
+
globalConfigPath,
|
|
39
|
+
readGlobalConfig
|
|
46
40
|
} from "./lib/utils/globalConfig.js";
|
|
47
41
|
import { getTeamsForUser } from "./lib/api.js";
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
42
|
+
import {
|
|
43
|
+
getProfileSelection,
|
|
44
|
+
initializeDefaultProfile,
|
|
45
|
+
legacyAuthForFirstProfile,
|
|
46
|
+
profileConfigPath,
|
|
47
|
+
readProfileAuth,
|
|
48
|
+
writeProfileCredentials
|
|
49
|
+
} from "./lib/profiles.js";
|
|
50
|
+
import {
|
|
51
|
+
getDeployKeySource,
|
|
52
|
+
loadProjectEnvironmentVariables
|
|
53
|
+
} from "./lib/envvars.js";
|
|
54
|
+
import { bindProfileToDirectory } from "./lib/profileManagement.js";
|
|
55
|
+
import { warnIfDeployKeyOverridesLogin } from "./lib/deployKeyWarning.js";
|
|
59
56
|
async function reportDeployKeyInWorkingDirectory(ctx, auth) {
|
|
60
57
|
if (auth === null || auth.kind === "accessToken" || auth.kind === "previewDeployKey") {
|
|
61
58
|
return;
|
|
62
59
|
}
|
|
63
60
|
const key = auth.kind === "projectKey" ? auth.projectKey : auth.deploymentKey;
|
|
64
|
-
const envVarName
|
|
65
|
-
const envFile = deployKeyEnvFile(ctx, envVarName, key);
|
|
66
|
-
const source = envFile ?? "shell environment";
|
|
61
|
+
const { envVarName, source } = getDeployKeySource(key);
|
|
67
62
|
const secret = key.slice(key.indexOf("|") + 1);
|
|
68
63
|
if (await isAuthorizedHeader(ctx, `Bearer ${secret}`)) {
|
|
69
64
|
logMessage(`Working Directory: Valid ${envVarName} in ${source}`);
|
|
@@ -71,103 +66,221 @@ async function reportDeployKeyInWorkingDirectory(ctx, auth) {
|
|
|
71
66
|
}
|
|
72
67
|
logWarning(`Working Directory: Invalid ${envVarName} in ${source}`);
|
|
73
68
|
}
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
if (globalConfig === null) {
|
|
83
|
-
logMessage(`No Convex account token found in: ${globalConfigPath()}`);
|
|
84
|
-
logMessage("Status: Not logged in");
|
|
85
|
-
await reportDeployKeyInWorkingDirectory(ctx, auth);
|
|
86
|
-
return;
|
|
87
|
-
}
|
|
88
|
-
logMessage(`Convex account token found in: ${globalConfigPath()}`);
|
|
89
|
-
const accessToken = globalConfig.accessToken;
|
|
90
|
-
if (!await isAuthorizedHeader(ctx, `Bearer ${accessToken}`)) {
|
|
91
|
-
logMessage("Status: Not logged in");
|
|
92
|
-
await reportDeployKeyInWorkingDirectory(ctx, auth);
|
|
93
|
-
return;
|
|
94
|
-
}
|
|
95
|
-
logMessage("Status: Logged in");
|
|
96
|
-
ctx._updateBigBrainAuth({
|
|
97
|
-
kind: "accessToken",
|
|
98
|
-
header: `Bearer ${accessToken}`,
|
|
99
|
-
accessToken
|
|
100
|
-
});
|
|
101
|
-
const teams = await getTeamsForUser(ctx);
|
|
102
|
-
logMessage(
|
|
103
|
-
`Teams: ${teams.length} team${teams.length === 1 ? "" : "s"} accessible`
|
|
104
|
-
);
|
|
105
|
-
for (const team of teams) {
|
|
106
|
-
logMessage(` - ${team.name} (${team.slug})`);
|
|
107
|
-
}
|
|
108
|
-
await reportDeployKeyInWorkingDirectory(ctx, auth);
|
|
109
|
-
});
|
|
110
|
-
export const login = new Command("login").description("Login to Convex").allowExcessArguments(false).option(
|
|
111
|
-
"--device-name <name>",
|
|
112
|
-
"Provide a name for the device being authorized"
|
|
113
|
-
).option(
|
|
114
|
-
"-f, --force",
|
|
115
|
-
"Proceed with login even if a valid access token already exists for this device"
|
|
116
|
-
).option(
|
|
117
|
-
"--no-open",
|
|
118
|
-
"Don't automatically open the login link in the default browser"
|
|
119
|
-
).addOption(
|
|
120
|
-
new Option(
|
|
121
|
-
"--login-flow <mode>",
|
|
122
|
-
`How to log in; defaults to guessing based on the environment.`
|
|
123
|
-
).choices(["paste", "auto", "poll"]).default("auto")
|
|
124
|
-
).addOption(new Option("--link-deployments").hideHelp()).addOption(new Option("--override-auth-url <url>").hideHelp()).addOption(new Option("--override-auth-client <id>").hideHelp()).addOption(new Option("--override-auth-username <username>").hideHelp()).addOption(new Option("--override-auth-password <password>").hideHelp()).addOption(new Option("--override-access-token <token>").hideHelp()).addOption(new Option("--accept-opt-ins").hideHelp()).addOption(new Option("--dump-access-token").hideHelp()).addOption(new Option("--check-login").hideHelp()).addOption(
|
|
125
|
-
new Option(
|
|
126
|
-
"--vercel",
|
|
127
|
-
"Redirect to Vercel SSO integration for login"
|
|
128
|
-
).hideHelp()
|
|
129
|
-
).addOption(new Option("--vercel-override <slug>").hideHelp()).addCommand(loginStatus).addHelpCommand(false).action(async (options, cmd) => {
|
|
130
|
-
const ctx = await oneoffContext({
|
|
131
|
-
url: void 0,
|
|
132
|
-
adminKey: void 0,
|
|
133
|
-
envFile: void 0
|
|
134
|
-
});
|
|
135
|
-
if (!options.force && await checkAuthorization(ctx, !!options.acceptOptIns)) {
|
|
136
|
-
logFinishedStep(
|
|
137
|
-
"This device has previously been authorized and is ready for use with Convex."
|
|
138
|
-
);
|
|
139
|
-
await handleLinkingDeployments(ctx, {
|
|
140
|
-
interactive: !!options.linkDeployments
|
|
69
|
+
function createLoginStatusCommand() {
|
|
70
|
+
return new Command("status").description(
|
|
71
|
+
"Check login status and list accessible teams. Exits with code 1 when not logged in."
|
|
72
|
+
).allowExcessArguments(false).action(async () => {
|
|
73
|
+
const ctx = await oneoffContext({
|
|
74
|
+
url: void 0,
|
|
75
|
+
adminKey: void 0,
|
|
76
|
+
envFile: void 0
|
|
141
77
|
});
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
78
|
+
const auth = ctx.bigBrainAuth();
|
|
79
|
+
let accountAuth = auth?.kind === "accessToken" ? auth : null;
|
|
80
|
+
if (accountAuth === null) {
|
|
81
|
+
const profile = await getProfileSelection(ctx);
|
|
82
|
+
if (profile !== null) {
|
|
83
|
+
accountAuth = await readProfileAuth(ctx, profile);
|
|
84
|
+
} else {
|
|
85
|
+
const globalConfig = readGlobalConfig(ctx);
|
|
86
|
+
if (globalConfig !== null) {
|
|
87
|
+
accountAuth = {
|
|
88
|
+
kind: "accessToken",
|
|
89
|
+
accessToken: globalConfig.accessToken,
|
|
90
|
+
header: `Bearer ${globalConfig.accessToken}`,
|
|
91
|
+
profile: null
|
|
92
|
+
};
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
if (accountAuth === null) {
|
|
97
|
+
logMessage(`No Convex account token found in: ${globalConfigPath()}`);
|
|
98
|
+
logMessage("Status: Not logged in");
|
|
99
|
+
await reportDeployKeyInWorkingDirectory(ctx, auth);
|
|
100
|
+
return await ctx.crash({
|
|
148
101
|
exitCode: 1,
|
|
149
102
|
errorType: "fatal",
|
|
150
|
-
|
|
151
|
-
printedMessage: "You are not logged in."
|
|
103
|
+
printedMessage: null
|
|
152
104
|
});
|
|
153
105
|
}
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
106
|
+
warnIfDeployKeyOverridesLogin(ctx, true);
|
|
107
|
+
if (accountAuth.profile) {
|
|
108
|
+
logMessage(`Account: ${accountAuth.profile.name}`);
|
|
109
|
+
logMessage(`Selected by: ${accountAuth.profile.source}`);
|
|
110
|
+
logMessage(
|
|
111
|
+
`Convex account token found in: ${profileConfigPath(accountAuth.profile.name)}`
|
|
112
|
+
);
|
|
113
|
+
} else if (process.env.CONVEX_OVERRIDE_ACCESS_TOKEN) {
|
|
114
|
+
logMessage("Using CONVEX_OVERRIDE_ACCESS_TOKEN from the environment");
|
|
115
|
+
} else {
|
|
116
|
+
logMessage(`Convex account token found in: ${globalConfigPath()}`);
|
|
117
|
+
}
|
|
118
|
+
if (!await isAuthorizedHeader(ctx, accountAuth.header)) {
|
|
119
|
+
logMessage("Status: Not logged in");
|
|
120
|
+
await reportDeployKeyInWorkingDirectory(ctx, auth);
|
|
121
|
+
return await ctx.crash({
|
|
122
|
+
exitCode: 1,
|
|
123
|
+
errorType: "fatal",
|
|
124
|
+
printedMessage: null
|
|
125
|
+
});
|
|
126
|
+
}
|
|
127
|
+
logMessage("Status: Logged in");
|
|
128
|
+
ctx._updateBigBrainAuth(accountAuth);
|
|
129
|
+
const teams = await getTeamsForUser(ctx);
|
|
130
|
+
logMessage(
|
|
131
|
+
`Teams: ${teams.length} team${teams.length === 1 ? "" : "s"} accessible`
|
|
158
132
|
);
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
anonymousId: uuid,
|
|
164
|
-
vercel: options.vercel,
|
|
165
|
-
vercelOverride: options.vercelOverride
|
|
133
|
+
for (const team of teams) {
|
|
134
|
+
logMessage(` - ${team.name} (${team.slug})`);
|
|
135
|
+
}
|
|
136
|
+
await reportDeployKeyInWorkingDirectory(ctx, auth);
|
|
166
137
|
});
|
|
167
|
-
|
|
168
|
-
|
|
138
|
+
}
|
|
139
|
+
export function createLoginCommand() {
|
|
140
|
+
return new Command("login").description("Login to Convex").allowExcessArguments(false).option("--account <name>", "Log in to a named account").option(
|
|
141
|
+
"--select",
|
|
142
|
+
"Select this account for this directory and its subdirectories"
|
|
143
|
+
).option(
|
|
144
|
+
"--device-name <name>",
|
|
145
|
+
"Provide a name for the device being authorized"
|
|
146
|
+
).option(
|
|
147
|
+
"-f, --force",
|
|
148
|
+
"Proceed with login even if a valid access token already exists for this device"
|
|
149
|
+
).option(
|
|
150
|
+
"--no-open",
|
|
151
|
+
"Don't automatically open the login link in the default browser"
|
|
152
|
+
).addOption(
|
|
153
|
+
new Option(
|
|
154
|
+
"--login-flow <mode>",
|
|
155
|
+
`How to log in; defaults to guessing based on the environment.`
|
|
156
|
+
).choices(["paste", "auto", "poll"]).default("auto")
|
|
157
|
+
).addOption(new Option("--link-deployments").hideHelp()).addOption(new Option("--override-auth-url <url>").hideHelp()).addOption(new Option("--override-auth-client <id>").hideHelp()).addOption(new Option("--override-auth-username <username>").hideHelp()).addOption(new Option("--override-auth-password <password>").hideHelp()).addOption(new Option("--override-access-token <token>").hideHelp()).addOption(new Option("--accept-opt-ins").hideHelp()).addOption(new Option("--dump-access-token").hideHelp()).addOption(new Option("--check-login").hideHelp()).addOption(
|
|
158
|
+
new Option(
|
|
159
|
+
"--vercel",
|
|
160
|
+
"Redirect to Vercel SSO integration for login"
|
|
161
|
+
).hideHelp()
|
|
162
|
+
).addOption(new Option("--vercel-override <slug>").hideHelp()).addCommand(createLoginStatusCommand()).addHelpCommand(false).action(async (options, cmd) => {
|
|
163
|
+
const ctx = await oneoffContext({
|
|
164
|
+
url: void 0,
|
|
165
|
+
adminKey: void 0,
|
|
166
|
+
envFile: void 0,
|
|
167
|
+
skipAuth: true
|
|
168
|
+
});
|
|
169
|
+
const profile = await getProfileSelection(ctx, {
|
|
170
|
+
name: options.account
|
|
171
|
+
});
|
|
172
|
+
if (options.select && profile === null) {
|
|
173
|
+
cmd.error("Pass --account <name> with --select to name this login.");
|
|
174
|
+
}
|
|
175
|
+
loadProjectEnvironmentVariables();
|
|
176
|
+
let namingLegacyAccount = false;
|
|
177
|
+
if (profile !== null) {
|
|
178
|
+
let auth2 = options.force ? null : await readProfileAuth(ctx, profile, true);
|
|
179
|
+
if (auth2 === null && !options.force && !options.overrideAccessToken && !options.overrideAuthUsername && !process.env.CONVEX_OVERRIDE_ACCESS_TOKEN) {
|
|
180
|
+
auth2 = await legacyAuthForFirstProfile(ctx, profile);
|
|
181
|
+
namingLegacyAccount = auth2 !== null;
|
|
182
|
+
}
|
|
183
|
+
ctx._updateBigBrainAuth(auth2);
|
|
184
|
+
} else {
|
|
185
|
+
const accessToken = process.env.CONVEX_OVERRIDE_ACCESS_TOKEN || readGlobalConfig(ctx)?.accessToken;
|
|
186
|
+
ctx._updateBigBrainAuth(
|
|
187
|
+
accessToken ? {
|
|
188
|
+
kind: "accessToken",
|
|
189
|
+
accessToken,
|
|
190
|
+
header: `Bearer ${accessToken}`,
|
|
191
|
+
profile: null
|
|
192
|
+
} : null
|
|
193
|
+
);
|
|
194
|
+
}
|
|
195
|
+
warnIfDeployKeyOverridesLogin(ctx, ctx.bigBrainAuth() !== null);
|
|
196
|
+
if (!options.force && await checkAuthorization(ctx, !!options.acceptOptIns)) {
|
|
197
|
+
const auth2 = ctx.bigBrainAuth();
|
|
198
|
+
if (namingLegacyAccount && profile !== null && auth2?.kind === "accessToken") {
|
|
199
|
+
await writeProfileCredentials(
|
|
200
|
+
ctx,
|
|
201
|
+
profile.name,
|
|
202
|
+
auth2.accessToken,
|
|
203
|
+
{},
|
|
204
|
+
null
|
|
205
|
+
);
|
|
206
|
+
}
|
|
207
|
+
logFinishedStep(
|
|
208
|
+
profile !== null ? namingLegacyAccount ? `Saved account "${profile.name}" using your existing login.` : `Account "${profile.name}" is already logged in.` : "This device has previously been authorized and is ready for use with Convex."
|
|
209
|
+
);
|
|
210
|
+
if (profile !== null && auth2?.kind === "accessToken") {
|
|
211
|
+
await initializeDefaultProfile(
|
|
212
|
+
ctx,
|
|
213
|
+
profile,
|
|
214
|
+
namingLegacyAccount,
|
|
215
|
+
!!options.select
|
|
216
|
+
);
|
|
217
|
+
}
|
|
218
|
+
if (options.select && profile !== null) {
|
|
219
|
+
if (!await bindProfileToDirectory(ctx, profile.name)) {
|
|
220
|
+
return await ctx.crash({
|
|
221
|
+
exitCode: 1,
|
|
222
|
+
errorType: "fatal",
|
|
223
|
+
printedMessage: `Logged in as "${profile.name}", but the directory selection could not be changed.`
|
|
224
|
+
});
|
|
225
|
+
}
|
|
226
|
+
}
|
|
227
|
+
await handleLinkingDeployments(ctx, {
|
|
228
|
+
interactive: !!options.linkDeployments
|
|
229
|
+
});
|
|
230
|
+
return;
|
|
231
|
+
}
|
|
232
|
+
if (!options.force && options.checkLogin) {
|
|
233
|
+
const isLoggedIn = await checkAuthorization(
|
|
234
|
+
ctx,
|
|
235
|
+
!!options.acceptOptIns
|
|
236
|
+
);
|
|
237
|
+
if (!isLoggedIn) {
|
|
238
|
+
return ctx.crash({
|
|
239
|
+
exitCode: 1,
|
|
240
|
+
errorType: "fatal",
|
|
241
|
+
errForSentry: "You are not logged in.",
|
|
242
|
+
printedMessage: "You are not logged in."
|
|
243
|
+
});
|
|
244
|
+
}
|
|
245
|
+
}
|
|
246
|
+
if (!!options.overrideAuthUsername !== !!options.overrideAuthPassword) {
|
|
247
|
+
cmd.error(
|
|
248
|
+
"If overriding credentials, both username and password must be provided"
|
|
249
|
+
);
|
|
250
|
+
}
|
|
251
|
+
const uuid = loadUuidForAnonymousUser(ctx);
|
|
252
|
+
await performLogin(ctx, {
|
|
253
|
+
...options,
|
|
254
|
+
profile,
|
|
255
|
+
anonymousId: uuid,
|
|
256
|
+
vercel: options.vercel,
|
|
257
|
+
vercelOverride: options.vercelOverride
|
|
258
|
+
});
|
|
259
|
+
const auth = ctx.bigBrainAuth();
|
|
260
|
+
if (profile !== null && auth?.kind === "accessToken") {
|
|
261
|
+
await initializeDefaultProfile(
|
|
262
|
+
ctx,
|
|
263
|
+
profile,
|
|
264
|
+
namingLegacyAccount,
|
|
265
|
+
!!options.select
|
|
266
|
+
);
|
|
267
|
+
}
|
|
268
|
+
if (options.select && profile !== null) {
|
|
269
|
+
if (!await bindProfileToDirectory(ctx, profile.name)) {
|
|
270
|
+
return await ctx.crash({
|
|
271
|
+
exitCode: 1,
|
|
272
|
+
errorType: "fatal",
|
|
273
|
+
printedMessage: `Logged in as "${profile.name}", but the directory selection could not be changed.`
|
|
274
|
+
});
|
|
275
|
+
}
|
|
276
|
+
}
|
|
277
|
+
warnIfDeployKeyOverridesLogin(ctx, true);
|
|
278
|
+
await handleLinkingDeployments(ctx, {
|
|
279
|
+
interactive: !!options.linkDeployments
|
|
280
|
+
});
|
|
169
281
|
});
|
|
170
|
-
}
|
|
282
|
+
}
|
|
283
|
+
export const login = createLoginCommand();
|
|
171
284
|
async function handleLinkingDeployments(ctx, args) {
|
|
172
285
|
if (!shouldAllowAnonymousDevelopment()) {
|
|
173
286
|
return;
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../../src/cli/login.ts"],
|
|
4
|
-
"sourcesContent": ["import { Command, Option } from \"@commander-js/extra-typings\";\nimport * as dotenv from \"dotenv\";\nimport { BigBrainAuth, Context, oneoffContext } from \"../bundler/context.js\";\nimport { logFinishedStep, logMessage, logWarning } from \"../bundler/log.js\";\nimport {\n checkAuthorization,\n isAuthorizedHeader,\n performLogin,\n} from \"./lib/login.js\";\nimport {\n loadProjectLocalConfig,\n loadUuidForAnonymousUser,\n} from \"./lib/localDeployment/filePaths.js\";\nimport {\n handleLinkToProject,\n listLegacyAnonymousDeployments,\n} from \"./lib/localDeployment/anonymous.js\";\nimport {\n DASHBOARD_HOST,\n deploymentDashboardUrlPage,\n teamDashboardUrl,\n} from \"./lib/dashboard.js\";\nimport { promptSearch, promptYesNo } from \"./lib/utils/prompts.js\";\nimport {\n CONVEX_DEPLOY_KEY_ENV_VAR_NAME,\n CONVEX_DEPLOYMENT_TOKEN_ENV_VAR_NAME,\n ENV_VAR_FILE_PATH,\n validateOrSelectTeam,\n} from \"./lib/utils/utils.js\";\nimport {\n selectProject,\n updateEnvAndConfigForDeploymentSelection,\n} from \"./configure.js\";\nimport {\n getDeploymentSelection,\n shouldAllowAnonymousDevelopment,\n} from \"./lib/deploymentSelection.js\";\nimport {\n isAnonymousDeployment,\n removeAnonymousPrefix,\n} from \"./lib/deployment.js\";\nimport {\n readGlobalConfig,\n globalConfigPath,\n} from \"./lib/utils/globalConfig.js\";\nimport { getTeamsForUser } from \"./lib/api.js\";\n\n/**\n * The env file a deploy key came from, or null if it came from the shell.\n * `dotenv` doesn't overwrite variables that are already set, so a value from\n * the shell outranks these files -- match on the value to tell them apart.\n */\nfunction deployKeyEnvFile(\n ctx: Context,\n envVarName: string,\n value: string,\n): string | null {\n for (const file of [ENV_VAR_FILE_PATH, \".env\"]) {\n if (!ctx.fs.exists(file)) {\n continue;\n }\n if (dotenv.parse(ctx.fs.readUtf8File(file))[envVarName] === value) {\n return file;\n }\n }\n return null;\n}\n\n/**\n * A project or deployment key in the environment outranks the account token for\n * commands run from this directory, and an expired one makes the CLI look\n * logged out, so report it separately from the account.\n */\nasync function reportDeployKeyInWorkingDirectory(\n ctx: Context,\n auth: BigBrainAuth | null,\n) {\n // A preview deploy key is only used when there's no account token at all.\n if (\n auth === null ||\n auth.kind === \"accessToken\" ||\n auth.kind === \"previewDeployKey\"\n ) {\n return;\n }\n const key = auth.kind === \"projectKey\" ? auth.projectKey : auth.deploymentKey;\n const envVarName = process.env[CONVEX_DEPLOY_KEY_ENV_VAR_NAME]\n ? CONVEX_DEPLOY_KEY_ENV_VAR_NAME\n : CONVEX_DEPLOYMENT_TOKEN_ENV_VAR_NAME;\n const envFile = deployKeyEnvFile(ctx, envVarName, key);\n const source = envFile ?? \"shell environment\";\n // Big Brain only strips a `project:`/`team:` prefix itself, so a deployment\n // key authorizes only without its `dev:`/`prod:` prefix. Split on the first\n // `|` like the server does, so a secret containing one survives.\n const secret = key.slice(key.indexOf(\"|\") + 1);\n if (await isAuthorizedHeader(ctx, `Bearer ${secret}`)) {\n logMessage(`Working Directory: Valid ${envVarName} in ${source}`);\n return;\n }\n logWarning(`Working Directory: Invalid ${envVarName} in ${source}`);\n}\n\nconst loginStatus = new Command(\"status\")\n .description(\"Check login status and list accessible teams\")\n .allowExcessArguments(false)\n .action(async () => {\n const ctx = await oneoffContext({\n url: undefined,\n adminKey: undefined,\n envFile: undefined,\n });\n\n // `_updateBigBrainAuth` below replaces any deploy key in `ctx` with the\n // account token, so read the deploy key first.\n const auth = ctx.bigBrainAuth();\n\n const globalConfig = readGlobalConfig(ctx);\n if (globalConfig === null) {\n logMessage(`No Convex account token found in: ${globalConfigPath()}`);\n logMessage(\"Status: Not logged in\");\n await reportDeployKeyInWorkingDirectory(ctx, auth);\n return;\n }\n logMessage(`Convex account token found in: ${globalConfigPath()}`);\n\n const accessToken = globalConfig.accessToken;\n if (!(await isAuthorizedHeader(ctx, `Bearer ${accessToken}`))) {\n logMessage(\"Status: Not logged in\");\n await reportDeployKeyInWorkingDirectory(ctx, auth);\n return;\n }\n\n logMessage(\"Status: Logged in\");\n // Describe the account, even if a deploy key outranks it for other commands.\n ctx._updateBigBrainAuth({\n kind: \"accessToken\",\n header: `Bearer ${accessToken}`,\n accessToken,\n });\n const teams = await getTeamsForUser(ctx);\n logMessage(\n `Teams: ${teams.length} team${teams.length === 1 ? \"\" : \"s\"} accessible`,\n );\n for (const team of teams) {\n logMessage(` - ${team.name} (${team.slug})`);\n }\n await reportDeployKeyInWorkingDirectory(ctx, auth);\n });\n\nexport const login = new Command(\"login\")\n .description(\"Login to Convex\")\n .allowExcessArguments(false)\n .option(\n \"--device-name <name>\",\n \"Provide a name for the device being authorized\",\n )\n .option(\n \"-f, --force\",\n \"Proceed with login even if a valid access token already exists for this device\",\n )\n .option(\n \"--no-open\",\n \"Don't automatically open the login link in the default browser\",\n )\n .addOption(\n new Option(\n \"--login-flow <mode>\",\n `How to log in; defaults to guessing based on the environment.`,\n )\n .choices([\"paste\", \"auto\", \"poll\"] as const)\n .default(\"auto\" as const),\n )\n .addOption(new Option(\"--link-deployments\").hideHelp())\n // These options are hidden from the help/usage message, but allow overriding settings for testing.\n // Change the auth credentials with the auth provider\n .addOption(new Option(\"--override-auth-url <url>\").hideHelp())\n .addOption(new Option(\"--override-auth-client <id>\").hideHelp())\n .addOption(new Option(\"--override-auth-username <username>\").hideHelp())\n .addOption(new Option(\"--override-auth-password <password>\").hideHelp())\n // Skip the auth provider login and directly use this access token\n .addOption(new Option(\"--override-access-token <token>\").hideHelp())\n // Automatically accept opt ins without prompting\n .addOption(new Option(\"--accept-opt-ins\").hideHelp())\n // Dump the access token from the auth provider and skip authorization with Convex\n .addOption(new Option(\"--dump-access-token\").hideHelp())\n // Hidden option for tests to check if the user is logged in.\n .addOption(new Option(\"--check-login\").hideHelp())\n // Redirect to Vercel SSO integration URL\n .addOption(\n new Option(\n \"--vercel\",\n \"Redirect to Vercel SSO integration for login\",\n ).hideHelp(),\n )\n // Override the Vercel URL slug (defaults to 'convex')\n .addOption(new Option(\"--vercel-override <slug>\").hideHelp())\n .addCommand(loginStatus)\n .addHelpCommand(false)\n .action(async (options, cmd: Command) => {\n const ctx = await oneoffContext({\n url: undefined,\n adminKey: undefined,\n envFile: undefined,\n });\n if (\n !options.force &&\n (await checkAuthorization(ctx, !!options.acceptOptIns))\n ) {\n logFinishedStep(\n \"This device has previously been authorized and is ready for use with Convex.\",\n );\n await handleLinkingDeployments(ctx, {\n interactive: !!options.linkDeployments,\n });\n return;\n }\n if (!options.force && options.checkLogin) {\n const isLoggedIn = await checkAuthorization(ctx, !!options.acceptOptIns);\n if (!isLoggedIn) {\n return ctx.crash({\n exitCode: 1,\n errorType: \"fatal\",\n errForSentry: \"You are not logged in.\",\n printedMessage: \"You are not logged in.\",\n });\n }\n }\n if (!!options.overrideAuthUsername !== !!options.overrideAuthPassword) {\n cmd.error(\n \"If overriding credentials, both username and password must be provided\",\n );\n }\n\n const uuid = loadUuidForAnonymousUser(ctx);\n await performLogin(ctx, {\n ...options,\n anonymousId: uuid,\n vercel: options.vercel,\n vercelOverride: options.vercelOverride,\n });\n\n await handleLinkingDeployments(ctx, {\n interactive: !!options.linkDeployments,\n });\n });\n\nasync function handleLinkingDeployments(\n ctx: Context,\n args: {\n interactive: boolean;\n },\n) {\n if (!shouldAllowAnonymousDevelopment()) {\n return;\n }\n\n // Check for project-local anonymous deployment first - this takes priority\n const projectLocal = loadProjectLocalConfig(ctx);\n if (\n projectLocal !== null &&\n isAnonymousDeployment(projectLocal.deploymentName)\n ) {\n const shouldLink = await promptYesNo(ctx, {\n message: `Would you like to link your existing deployment to your account? (\"${projectLocal.deploymentName}\")`,\n default: true,\n });\n if (!shouldLink) {\n logMessage(\n \"Not linking your existing deployment. If you want to link it later, run `npx convex login --link-deployments`.\",\n );\n logMessage(\n `Visit ${DASHBOARD_HOST} or run \\`npx convex dev\\` to get started with your new account.`,\n );\n return;\n }\n\n const { dashboardUrl } = await linkSingleDeployment(\n ctx,\n projectLocal.deploymentName,\n projectLocal.deploymentName,\n );\n logFinishedStep(`Visit ${dashboardUrl} to get started.`);\n return;\n }\n\n // No project-local deployment - check for legacy deployments\n const legacyDeployments = listLegacyAnonymousDeployments(ctx);\n if (legacyDeployments.length === 0) {\n if (args.interactive) {\n logMessage(\n \"It doesn't look like you have any deployments to link. You can run `npx convex dev` to set up a new project or select an existing one.\",\n );\n }\n return;\n }\n\n // Get the currently configured deployment (if any) for env var updates\n const deploymentSelection = await getDeploymentSelection(ctx, {\n url: undefined,\n adminKey: undefined,\n envFile: undefined,\n });\n const configuredDeployment =\n deploymentSelection.kind === \"anonymous\"\n ? deploymentSelection.deploymentName\n : null;\n\n if (!args.interactive) {\n // Non-interactive: link all legacy deployments automatically\n const message = getMessage(legacyDeployments.map((d) => d.deploymentName));\n const createProjects = await promptYesNo(ctx, {\n message,\n default: true,\n });\n if (!createProjects) {\n logMessage(\n \"Not linking your existing deployments. If you want to link them later, run `npx convex login --link-deployments`.\",\n );\n logMessage(\n `Visit ${DASHBOARD_HOST} or run \\`npx convex dev\\` to get started with your new account.`,\n );\n return;\n }\n\n const {\n team: { slug: teamSlug },\n } = await validateOrSelectTeam(\n ctx,\n undefined,\n \"Choose a team for your deployments:\",\n );\n let dashboardUrl = teamDashboardUrl(teamSlug);\n for (const deployment of legacyDeployments) {\n const result = await linkSingleDeployment(\n ctx,\n deployment.deploymentName,\n configuredDeployment,\n { teamSlug, projectSlug: null },\n );\n if (deployment.deploymentName === configuredDeployment) {\n dashboardUrl = result.dashboardUrl;\n }\n }\n logFinishedStep(\n `Successfully linked your deployments! Visit ${dashboardUrl} to get started.`,\n );\n return;\n }\n\n // Interactive mode: let user choose which legacy deployments to link\n while (true) {\n const currentLegacyDeployments = listLegacyAnonymousDeployments(ctx);\n if (currentLegacyDeployments.length === 0) {\n logMessage(\"All deployments have been linked.\");\n break;\n }\n logMessage(\n getDeploymentListMessage(\n currentLegacyDeployments.map((d) => d.deploymentName),\n ),\n );\n const deploymentToLink = await promptSearch(ctx, {\n message: \"Which deployment would you like to link to your account?\",\n choices: currentLegacyDeployments.map((d) => ({\n name: d.deploymentName,\n value: d.deploymentName,\n })),\n });\n\n await linkSingleDeployment(ctx, deploymentToLink, configuredDeployment);\n\n const shouldContinue = await promptYesNo(ctx, {\n message: \"Would you like to link another deployment?\",\n default: true,\n });\n if (!shouldContinue) {\n break;\n }\n }\n}\n\n/**\n * Link a single deployment to a project, prompting for team and project selection.\n * Updates env vars if this is the currently configured deployment.\n */\nasync function linkSingleDeployment(\n ctx: Context,\n deploymentName: string,\n configuredDeployment: string | null,\n options?: {\n teamSlug?: string;\n projectSlug?: string | null;\n },\n): Promise<{ dashboardUrl: string }> {\n const { team } = await validateOrSelectTeam(\n ctx,\n options?.teamSlug,\n \"Choose a team for your deployment:\",\n );\n\n const projectSlug =\n options?.projectSlug ??\n (\n await selectProject(ctx, \"ask\", {\n team: team.slug,\n devDeployment: \"local\",\n defaultProjectName: removeAnonymousPrefix(deploymentName),\n })\n ).projectSlug;\n\n const linkedDeployment = await handleLinkToProject(ctx, {\n deploymentName,\n teamSlug: team.slug,\n teamId: team.id,\n projectSlug,\n });\n\n if (deploymentName === configuredDeployment) {\n await updateEnvAndConfigForDeploymentSelection(\n ctx,\n {\n url: linkedDeployment.deploymentUrl,\n deploymentName: linkedDeployment.deploymentName,\n teamSlug: team.slug,\n projectSlug: linkedDeployment.projectSlug,\n deploymentType: \"local\",\n },\n configuredDeployment,\n );\n }\n\n return {\n dashboardUrl: deploymentDashboardUrlPage(\n linkedDeployment.deploymentName,\n \"\",\n ),\n };\n}\n\nfunction getDeploymentListMessage(anonymousDeploymentNames: string[]) {\n let message = `You have ${anonymousDeploymentNames.length} existing deployments.`;\n message += `\\n\\nDeployments:`;\n for (const deploymentName of anonymousDeploymentNames) {\n message += `\\n- ${deploymentName}`;\n }\n return message;\n}\n\nfunction getMessage(anonymousDeploymentNames: string[]) {\n if (anonymousDeploymentNames.length === 1) {\n return `Would you like to link your existing deployment to your account? (\"${anonymousDeploymentNames[0]}\")`;\n }\n let message = `You have ${anonymousDeploymentNames.length} existing deployments. Would you like to link them to your account?`;\n message += `\\n\\nDeployments:`;\n for (const deploymentName of anonymousDeploymentNames) {\n message += `\\n- ${deploymentName}`;\n }\n message += `\\n\\nYou can alternatively run \\`npx convex login --link-deployments\\` to interactively choose which deployments to add.`;\n return message;\n}\n"],
|
|
5
|
-
"mappings": ";AAAA,SAAS,SAAS,cAAc;AAChC,
|
|
6
|
-
"names": []
|
|
4
|
+
"sourcesContent": ["import { Command, Option } from \"@commander-js/extra-typings\";\nimport { BigBrainAuth, Context, oneoffContext } from \"../bundler/context.js\";\nimport { logFinishedStep, logMessage, logWarning } from \"../bundler/log.js\";\nimport {\n checkAuthorization,\n isAuthorizedHeader,\n performLogin,\n} from \"./lib/login.js\";\nimport {\n loadProjectLocalConfig,\n loadUuidForAnonymousUser,\n} from \"./lib/localDeployment/filePaths.js\";\nimport {\n handleLinkToProject,\n listLegacyAnonymousDeployments,\n} from \"./lib/localDeployment/anonymous.js\";\nimport {\n DASHBOARD_HOST,\n deploymentDashboardUrlPage,\n teamDashboardUrl,\n} from \"./lib/dashboard.js\";\nimport { promptSearch, promptYesNo } from \"./lib/utils/prompts.js\";\nimport { validateOrSelectTeam } from \"./lib/utils/utils.js\";\nimport {\n selectProject,\n updateEnvAndConfigForDeploymentSelection,\n} from \"./configure.js\";\nimport {\n getDeploymentSelection,\n shouldAllowAnonymousDevelopment,\n} from \"./lib/deploymentSelection.js\";\nimport {\n isAnonymousDeployment,\n removeAnonymousPrefix,\n} from \"./lib/deployment.js\";\nimport {\n globalConfigPath,\n readGlobalConfig,\n} from \"./lib/utils/globalConfig.js\";\nimport { getTeamsForUser } from \"./lib/api.js\";\nimport {\n getProfileSelection,\n initializeDefaultProfile,\n legacyAuthForFirstProfile,\n profileConfigPath,\n readProfileAuth,\n writeProfileCredentials,\n} from \"./lib/profiles.js\";\nimport {\n getDeployKeySource,\n loadProjectEnvironmentVariables,\n} from \"./lib/envvars.js\";\nimport { bindProfileToDirectory } from \"./lib/profileManagement.js\";\nimport { warnIfDeployKeyOverridesLogin } from \"./lib/deployKeyWarning.js\";\n\n/**\n * A project or deployment key in the environment outranks the account token for\n * commands run from this directory, and an expired one makes the CLI look\n * logged out, so report it separately from the account.\n */\nasync function reportDeployKeyInWorkingDirectory(\n ctx: Context,\n auth: BigBrainAuth | null,\n) {\n // A preview deploy key is only used when there's no account token at all.\n if (\n auth === null ||\n auth.kind === \"accessToken\" ||\n auth.kind === \"previewDeployKey\"\n ) {\n return;\n }\n const key = auth.kind === \"projectKey\" ? auth.projectKey : auth.deploymentKey;\n const { envVarName, source } = getDeployKeySource(key);\n // Big Brain only strips a `project:`/`team:` prefix itself, so a deployment\n // key authorizes only without its `dev:`/`prod:` prefix. Split on the first\n // `|` like the server does, so a secret containing one survives.\n const secret = key.slice(key.indexOf(\"|\") + 1);\n if (await isAuthorizedHeader(ctx, `Bearer ${secret}`)) {\n logMessage(`Working Directory: Valid ${envVarName} in ${source}`);\n return;\n }\n logWarning(`Working Directory: Invalid ${envVarName} in ${source}`);\n}\n\nfunction createLoginStatusCommand() {\n return new Command(\"status\")\n .description(\n \"Check login status and list accessible teams. Exits with code 1 when not logged in.\",\n )\n .allowExcessArguments(false)\n .action(async () => {\n const ctx = await oneoffContext({\n url: undefined,\n adminKey: undefined,\n envFile: undefined,\n });\n\n // Keep the directory's deploy key for separate reporting after selecting\n // the account whose teams will be listed.\n const auth = ctx.bigBrainAuth();\n let accountAuth = auth?.kind === \"accessToken\" ? auth : null;\n if (accountAuth === null) {\n const profile = await getProfileSelection(ctx);\n if (profile !== null) {\n accountAuth = await readProfileAuth(ctx, profile);\n } else {\n const globalConfig = readGlobalConfig(ctx);\n if (globalConfig !== null) {\n accountAuth = {\n kind: \"accessToken\",\n accessToken: globalConfig.accessToken,\n header: `Bearer ${globalConfig.accessToken}`,\n profile: null,\n };\n }\n }\n }\n if (accountAuth === null) {\n logMessage(`No Convex account token found in: ${globalConfigPath()}`);\n logMessage(\"Status: Not logged in\");\n await reportDeployKeyInWorkingDirectory(ctx, auth);\n return await ctx.crash({\n exitCode: 1,\n errorType: \"fatal\",\n printedMessage: null,\n });\n }\n warnIfDeployKeyOverridesLogin(ctx, true);\n if (accountAuth.profile) {\n logMessage(`Account: ${accountAuth.profile.name}`);\n logMessage(`Selected by: ${accountAuth.profile.source}`);\n logMessage(\n `Convex account token found in: ${profileConfigPath(accountAuth.profile.name)}`,\n );\n } else if (process.env.CONVEX_OVERRIDE_ACCESS_TOKEN) {\n logMessage(\"Using CONVEX_OVERRIDE_ACCESS_TOKEN from the environment\");\n } else {\n logMessage(`Convex account token found in: ${globalConfigPath()}`);\n }\n\n if (!(await isAuthorizedHeader(ctx, accountAuth.header))) {\n logMessage(\"Status: Not logged in\");\n await reportDeployKeyInWorkingDirectory(ctx, auth);\n return await ctx.crash({\n exitCode: 1,\n errorType: \"fatal\",\n printedMessage: null,\n });\n }\n\n logMessage(\"Status: Logged in\");\n ctx._updateBigBrainAuth(accountAuth);\n const teams = await getTeamsForUser(ctx);\n logMessage(\n `Teams: ${teams.length} team${teams.length === 1 ? \"\" : \"s\"} accessible`,\n );\n for (const team of teams) {\n logMessage(` - ${team.name} (${team.slug})`);\n }\n await reportDeployKeyInWorkingDirectory(ctx, auth);\n });\n}\n\nexport function createLoginCommand() {\n return (\n new Command(\"login\")\n .description(\"Login to Convex\")\n .allowExcessArguments(false)\n .option(\"--account <name>\", \"Log in to a named account\")\n .option(\n \"--select\",\n \"Select this account for this directory and its subdirectories\",\n )\n .option(\n \"--device-name <name>\",\n \"Provide a name for the device being authorized\",\n )\n .option(\n \"-f, --force\",\n \"Proceed with login even if a valid access token already exists for this device\",\n )\n .option(\n \"--no-open\",\n \"Don't automatically open the login link in the default browser\",\n )\n .addOption(\n new Option(\n \"--login-flow <mode>\",\n `How to log in; defaults to guessing based on the environment.`,\n )\n .choices([\"paste\", \"auto\", \"poll\"] as const)\n .default(\"auto\" as const),\n )\n .addOption(new Option(\"--link-deployments\").hideHelp())\n // These options are hidden from the help/usage message, but allow overriding settings for testing.\n // Change the auth credentials with the auth provider\n .addOption(new Option(\"--override-auth-url <url>\").hideHelp())\n .addOption(new Option(\"--override-auth-client <id>\").hideHelp())\n .addOption(new Option(\"--override-auth-username <username>\").hideHelp())\n .addOption(new Option(\"--override-auth-password <password>\").hideHelp())\n // Skip the auth provider login and directly use this access token\n .addOption(new Option(\"--override-access-token <token>\").hideHelp())\n // Automatically accept opt ins without prompting\n .addOption(new Option(\"--accept-opt-ins\").hideHelp())\n // Dump the access token from the auth provider and skip authorization with Convex\n .addOption(new Option(\"--dump-access-token\").hideHelp())\n // Hidden option for tests to check if the user is logged in.\n .addOption(new Option(\"--check-login\").hideHelp())\n // Redirect to Vercel SSO integration URL\n .addOption(\n new Option(\n \"--vercel\",\n \"Redirect to Vercel SSO integration for login\",\n ).hideHelp(),\n )\n // Override the Vercel URL slug (defaults to 'convex')\n .addOption(new Option(\"--vercel-override <slug>\").hideHelp())\n .addCommand(createLoginStatusCommand())\n .addHelpCommand(false)\n .action(async (options, cmd: Command) => {\n const ctx = await oneoffContext({\n url: undefined,\n adminKey: undefined,\n envFile: undefined,\n skipAuth: true,\n });\n const profile = await getProfileSelection(ctx, {\n name: options.account,\n });\n if (options.select && profile === null) {\n cmd.error(\"Pass --account <name> with --select to name this login.\");\n }\n loadProjectEnvironmentVariables();\n let namingLegacyAccount = false;\n if (profile !== null) {\n let auth = options.force\n ? null\n : await readProfileAuth(ctx, profile, true);\n if (\n auth === null &&\n !options.force &&\n !options.overrideAccessToken &&\n !options.overrideAuthUsername &&\n !process.env.CONVEX_OVERRIDE_ACCESS_TOKEN\n ) {\n auth = await legacyAuthForFirstProfile(ctx, profile);\n namingLegacyAccount = auth !== null;\n }\n ctx._updateBigBrainAuth(auth);\n } else {\n const accessToken =\n process.env.CONVEX_OVERRIDE_ACCESS_TOKEN ||\n readGlobalConfig(ctx)?.accessToken;\n ctx._updateBigBrainAuth(\n accessToken\n ? {\n kind: \"accessToken\",\n accessToken,\n header: `Bearer ${accessToken}`,\n profile: null,\n }\n : null,\n );\n }\n warnIfDeployKeyOverridesLogin(ctx, ctx.bigBrainAuth() !== null);\n if (\n !options.force &&\n (await checkAuthorization(ctx, !!options.acceptOptIns))\n ) {\n const auth = ctx.bigBrainAuth();\n if (\n namingLegacyAccount &&\n profile !== null &&\n auth?.kind === \"accessToken\"\n ) {\n await writeProfileCredentials(\n ctx,\n profile.name,\n auth.accessToken,\n {},\n null,\n );\n }\n logFinishedStep(\n profile !== null\n ? namingLegacyAccount\n ? `Saved account \"${profile.name}\" using your existing login.`\n : `Account \"${profile.name}\" is already logged in.`\n : \"This device has previously been authorized and is ready for use with Convex.\",\n );\n if (profile !== null && auth?.kind === \"accessToken\") {\n await initializeDefaultProfile(\n ctx,\n profile,\n namingLegacyAccount,\n !!options.select,\n );\n }\n if (options.select && profile !== null) {\n if (!(await bindProfileToDirectory(ctx, profile.name))) {\n return await ctx.crash({\n exitCode: 1,\n errorType: \"fatal\",\n printedMessage: `Logged in as \"${profile.name}\", but the directory selection could not be changed.`,\n });\n }\n }\n await handleLinkingDeployments(ctx, {\n interactive: !!options.linkDeployments,\n });\n return;\n }\n if (!options.force && options.checkLogin) {\n const isLoggedIn = await checkAuthorization(\n ctx,\n !!options.acceptOptIns,\n );\n if (!isLoggedIn) {\n return ctx.crash({\n exitCode: 1,\n errorType: \"fatal\",\n errForSentry: \"You are not logged in.\",\n printedMessage: \"You are not logged in.\",\n });\n }\n }\n if (!!options.overrideAuthUsername !== !!options.overrideAuthPassword) {\n cmd.error(\n \"If overriding credentials, both username and password must be provided\",\n );\n }\n\n const uuid = loadUuidForAnonymousUser(ctx);\n await performLogin(ctx, {\n ...options,\n profile,\n anonymousId: uuid,\n vercel: options.vercel,\n vercelOverride: options.vercelOverride,\n });\n\n const auth = ctx.bigBrainAuth();\n if (profile !== null && auth?.kind === \"accessToken\") {\n await initializeDefaultProfile(\n ctx,\n profile,\n namingLegacyAccount,\n !!options.select,\n );\n }\n if (options.select && profile !== null) {\n if (!(await bindProfileToDirectory(ctx, profile.name))) {\n return await ctx.crash({\n exitCode: 1,\n errorType: \"fatal\",\n printedMessage: `Logged in as \"${profile.name}\", but the directory selection could not be changed.`,\n });\n }\n }\n warnIfDeployKeyOverridesLogin(ctx, true);\n\n await handleLinkingDeployments(ctx, {\n interactive: !!options.linkDeployments,\n });\n })\n );\n}\n\nexport const login = createLoginCommand();\n\nasync function handleLinkingDeployments(\n ctx: Context,\n args: {\n interactive: boolean;\n },\n) {\n if (!shouldAllowAnonymousDevelopment()) {\n return;\n }\n\n // Check for project-local anonymous deployment first - this takes priority\n const projectLocal = loadProjectLocalConfig(ctx);\n if (\n projectLocal !== null &&\n isAnonymousDeployment(projectLocal.deploymentName)\n ) {\n const shouldLink = await promptYesNo(ctx, {\n message: `Would you like to link your existing deployment to your account? (\"${projectLocal.deploymentName}\")`,\n default: true,\n });\n if (!shouldLink) {\n logMessage(\n \"Not linking your existing deployment. If you want to link it later, run `npx convex login --link-deployments`.\",\n );\n logMessage(\n `Visit ${DASHBOARD_HOST} or run \\`npx convex dev\\` to get started with your new account.`,\n );\n return;\n }\n\n const { dashboardUrl } = await linkSingleDeployment(\n ctx,\n projectLocal.deploymentName,\n projectLocal.deploymentName,\n );\n logFinishedStep(`Visit ${dashboardUrl} to get started.`);\n return;\n }\n\n // No project-local deployment - check for legacy deployments\n const legacyDeployments = listLegacyAnonymousDeployments(ctx);\n if (legacyDeployments.length === 0) {\n if (args.interactive) {\n logMessage(\n \"It doesn't look like you have any deployments to link. You can run `npx convex dev` to set up a new project or select an existing one.\",\n );\n }\n return;\n }\n\n // Get the currently configured deployment (if any) for env var updates\n const deploymentSelection = await getDeploymentSelection(ctx, {\n url: undefined,\n adminKey: undefined,\n envFile: undefined,\n });\n const configuredDeployment =\n deploymentSelection.kind === \"anonymous\"\n ? deploymentSelection.deploymentName\n : null;\n\n if (!args.interactive) {\n // Non-interactive: link all legacy deployments automatically\n const message = getMessage(legacyDeployments.map((d) => d.deploymentName));\n const createProjects = await promptYesNo(ctx, {\n message,\n default: true,\n });\n if (!createProjects) {\n logMessage(\n \"Not linking your existing deployments. If you want to link them later, run `npx convex login --link-deployments`.\",\n );\n logMessage(\n `Visit ${DASHBOARD_HOST} or run \\`npx convex dev\\` to get started with your new account.`,\n );\n return;\n }\n\n const {\n team: { slug: teamSlug },\n } = await validateOrSelectTeam(\n ctx,\n undefined,\n \"Choose a team for your deployments:\",\n );\n let dashboardUrl = teamDashboardUrl(teamSlug);\n for (const deployment of legacyDeployments) {\n const result = await linkSingleDeployment(\n ctx,\n deployment.deploymentName,\n configuredDeployment,\n { teamSlug, projectSlug: null },\n );\n if (deployment.deploymentName === configuredDeployment) {\n dashboardUrl = result.dashboardUrl;\n }\n }\n logFinishedStep(\n `Successfully linked your deployments! Visit ${dashboardUrl} to get started.`,\n );\n return;\n }\n\n // Interactive mode: let user choose which legacy deployments to link\n while (true) {\n const currentLegacyDeployments = listLegacyAnonymousDeployments(ctx);\n if (currentLegacyDeployments.length === 0) {\n logMessage(\"All deployments have been linked.\");\n break;\n }\n logMessage(\n getDeploymentListMessage(\n currentLegacyDeployments.map((d) => d.deploymentName),\n ),\n );\n const deploymentToLink = await promptSearch(ctx, {\n message: \"Which deployment would you like to link to your account?\",\n choices: currentLegacyDeployments.map((d) => ({\n name: d.deploymentName,\n value: d.deploymentName,\n })),\n });\n\n await linkSingleDeployment(ctx, deploymentToLink, configuredDeployment);\n\n const shouldContinue = await promptYesNo(ctx, {\n message: \"Would you like to link another deployment?\",\n default: true,\n });\n if (!shouldContinue) {\n break;\n }\n }\n}\n\n/**\n * Link a single deployment to a project, prompting for team and project selection.\n * Updates env vars if this is the currently configured deployment.\n */\nasync function linkSingleDeployment(\n ctx: Context,\n deploymentName: string,\n configuredDeployment: string | null,\n options?: {\n teamSlug?: string;\n projectSlug?: string | null;\n },\n): Promise<{ dashboardUrl: string }> {\n const { team } = await validateOrSelectTeam(\n ctx,\n options?.teamSlug,\n \"Choose a team for your deployment:\",\n );\n\n const projectSlug =\n options?.projectSlug ??\n (\n await selectProject(ctx, \"ask\", {\n team: team.slug,\n devDeployment: \"local\",\n defaultProjectName: removeAnonymousPrefix(deploymentName),\n })\n ).projectSlug;\n\n const linkedDeployment = await handleLinkToProject(ctx, {\n deploymentName,\n teamSlug: team.slug,\n teamId: team.id,\n projectSlug,\n });\n\n if (deploymentName === configuredDeployment) {\n await updateEnvAndConfigForDeploymentSelection(\n ctx,\n {\n url: linkedDeployment.deploymentUrl,\n deploymentName: linkedDeployment.deploymentName,\n teamSlug: team.slug,\n projectSlug: linkedDeployment.projectSlug,\n deploymentType: \"local\",\n },\n configuredDeployment,\n );\n }\n\n return {\n dashboardUrl: deploymentDashboardUrlPage(\n linkedDeployment.deploymentName,\n \"\",\n ),\n };\n}\n\nfunction getDeploymentListMessage(anonymousDeploymentNames: string[]) {\n let message = `You have ${anonymousDeploymentNames.length} existing deployments.`;\n message += `\\n\\nDeployments:`;\n for (const deploymentName of anonymousDeploymentNames) {\n message += `\\n- ${deploymentName}`;\n }\n return message;\n}\n\nfunction getMessage(anonymousDeploymentNames: string[]) {\n if (anonymousDeploymentNames.length === 1) {\n return `Would you like to link your existing deployment to your account? (\"${anonymousDeploymentNames[0]}\")`;\n }\n let message = `You have ${anonymousDeploymentNames.length} existing deployments. Would you like to link them to your account?`;\n message += `\\n\\nDeployments:`;\n for (const deploymentName of anonymousDeploymentNames) {\n message += `\\n- ${deploymentName}`;\n }\n message += `\\n\\nYou can alternatively run \\`npx convex login --link-deployments\\` to interactively choose which deployments to add.`;\n return message;\n}\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
|
+
"names": ["auth"]
|
|
7
7
|
}
|
package/dist/esm/cli/logout.js
CHANGED
|
@@ -1,20 +1,117 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
import { Command } from "@commander-js/extra-typings";
|
|
3
3
|
import { oneoffContext } from "../bundler/context.js";
|
|
4
|
-
import { logFinishedStep } from "../bundler/log.js";
|
|
4
|
+
import { logFinishedStep, logMessage, logWarning } from "../bundler/log.js";
|
|
5
5
|
import { recursivelyDelete } from "./lib/fsUtils.js";
|
|
6
6
|
import { globalConfigPath } from "./lib/utils/globalConfig.js";
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
7
|
+
import {
|
|
8
|
+
getProfileSelection,
|
|
9
|
+
profileConfigPath,
|
|
10
|
+
readProfileToken,
|
|
11
|
+
readProfileSettings,
|
|
12
|
+
updateProfileSettings,
|
|
13
|
+
savedProfileNames
|
|
14
|
+
} from "./lib/profiles.js";
|
|
15
|
+
import path from "path";
|
|
16
|
+
import { rootDirectory } from "./lib/utils/utils.js";
|
|
17
|
+
import { promptOptions } from "./lib/utils/prompts.js";
|
|
18
|
+
export function createLogoutCommand() {
|
|
19
|
+
return new Command("logout").description(
|
|
20
|
+
"Log out of the selected account. Directory selections are preserved."
|
|
21
|
+
).option("--account <name>", "Remove only this account's saved credentials").option(
|
|
22
|
+
"--all",
|
|
23
|
+
"Remove all saved account credentials, preserving account selections"
|
|
24
|
+
).allowExcessArguments(false).action(async (options, cmd) => {
|
|
25
|
+
if (options.all && options.account !== void 0)
|
|
26
|
+
cmd.error("Pass either --account or --all, not both.");
|
|
27
|
+
const ctx = await oneoffContext({
|
|
28
|
+
url: void 0,
|
|
29
|
+
adminKey: void 0,
|
|
30
|
+
envFile: void 0,
|
|
31
|
+
skipAuth: true
|
|
32
|
+
});
|
|
33
|
+
let all = !!options.all;
|
|
34
|
+
let profile = all ? null : await getProfileSelection(ctx, { name: options.account });
|
|
35
|
+
if (!all && options.account === void 0 && process.stdin.isTTY && profile !== null) {
|
|
36
|
+
const token = readProfileToken(ctx, profile.name);
|
|
37
|
+
const settings = await readProfileSettings(ctx);
|
|
38
|
+
const selectedName = profile.name;
|
|
39
|
+
const hasOtherLogins = savedProfileNames(ctx).some((name) => name !== selectedName) || typeof settings.accessToken === "string" && settings.accessToken.length > 0 && settings.accessToken !== token;
|
|
40
|
+
if (hasOtherLogins) {
|
|
41
|
+
const scope = await promptOptions(ctx, {
|
|
42
|
+
message: "Which saved logins would you like to log out of?",
|
|
43
|
+
choices: [
|
|
44
|
+
{ name: `Only account "${profile.name}"`, value: "profile" },
|
|
45
|
+
{ name: "All saved logins on this device", value: "all" }
|
|
46
|
+
],
|
|
47
|
+
default: "profile"
|
|
48
|
+
});
|
|
49
|
+
if (scope === "all") {
|
|
50
|
+
profile = null;
|
|
51
|
+
all = true;
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
let warnAboutLegacyCredentials = false;
|
|
56
|
+
if (profile !== null) {
|
|
57
|
+
const selectedName = profile.name;
|
|
58
|
+
await updateProfileSettings(
|
|
59
|
+
ctx,
|
|
60
|
+
(settings) => {
|
|
61
|
+
const token = readProfileToken(ctx, selectedName);
|
|
62
|
+
if (token !== null && settings.accessToken === token) {
|
|
63
|
+
delete settings.accessToken;
|
|
64
|
+
} else if (settings.defaultProfile === selectedName && typeof settings.accessToken === "string" && settings.accessToken.length > 0) {
|
|
65
|
+
warnAboutLegacyCredentials = true;
|
|
66
|
+
}
|
|
67
|
+
},
|
|
68
|
+
() => {
|
|
69
|
+
const configPath = profileConfigPath(selectedName);
|
|
70
|
+
recursivelyDelete(ctx, configPath, {
|
|
71
|
+
force: true,
|
|
72
|
+
followSymlinks: false
|
|
73
|
+
});
|
|
74
|
+
}
|
|
75
|
+
);
|
|
76
|
+
} else {
|
|
77
|
+
await updateProfileSettings(
|
|
78
|
+
ctx,
|
|
79
|
+
async (settings) => {
|
|
80
|
+
if (!all && savedProfileNames(ctx).length > 0 && settings.accessToken === void 0) {
|
|
81
|
+
return await ctx.crash({
|
|
82
|
+
exitCode: 1,
|
|
83
|
+
errorType: "fatal",
|
|
84
|
+
printedMessage: "No account is selected. Use `npx convex logout --account <name>` or `npx convex logout --all`."
|
|
85
|
+
});
|
|
86
|
+
}
|
|
87
|
+
delete settings.accessToken;
|
|
88
|
+
},
|
|
89
|
+
() => {
|
|
90
|
+
const profilesDir = path.join(rootDirectory(), "profiles");
|
|
91
|
+
if (all)
|
|
92
|
+
recursivelyDelete(ctx, profilesDir, {
|
|
93
|
+
force: true,
|
|
94
|
+
followSymlinks: false
|
|
95
|
+
});
|
|
96
|
+
}
|
|
97
|
+
);
|
|
98
|
+
}
|
|
99
|
+
logFinishedStep(
|
|
100
|
+
profile !== null ? `Removed credentials for account "${profile.name}".
|
|
101
|
+
Run \`npx convex login --account ${profile.name}\` to log in.` : `${all ? "Removed all saved Convex account credentials." : "Removed your saved Convex login credentials."}
|
|
102
|
+
Run \`npx convex login\` to log in.`
|
|
103
|
+
);
|
|
104
|
+
if (warnAboutLegacyCredentials) {
|
|
105
|
+
logWarning(
|
|
106
|
+
`Saved credentials remain in ${globalConfigPath()} for older CLI versions. To remove all saved account credentials, run \`npx convex logout --all\`.`
|
|
107
|
+
);
|
|
108
|
+
}
|
|
109
|
+
if (process.env.CONVEX_OVERRIDE_ACCESS_TOKEN) {
|
|
110
|
+
logMessage(
|
|
111
|
+
"CONVEX_OVERRIDE_ACCESS_TOKEN still supplies credentials. Unset it to stop using that token."
|
|
112
|
+
);
|
|
113
|
+
}
|
|
12
114
|
});
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
}
|
|
16
|
-
logFinishedStep(
|
|
17
|
-
"You have been logged out of Convex.\n Run `npx convex dev` to log in."
|
|
18
|
-
);
|
|
19
|
-
});
|
|
115
|
+
}
|
|
116
|
+
export const logout = createLogoutCommand();
|
|
20
117
|
//# sourceMappingURL=logout.js.map
|