convex 1.48.0 → 1.49.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/CHANGELOG.md +7 -0
- 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/accountManagement.js +196 -0
- package/dist/cjs/cli/lib/accountManagement.js.map +7 -0
- package/dist/cjs/cli/lib/accounts.js +334 -0
- package/dist/cjs/cli/lib/accounts.js.map +7 -0
- 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 +42 -4
- package/dist/cjs/cli/lib/login.js.map +3 -3
- 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/plugins/claudeCode.js +188 -0
- package/dist/cjs/cli/lib/plugins/claudeCode.js.map +7 -0
- package/dist/cjs/cli/lib/plugins/codex.js +167 -0
- package/dist/cjs/cli/lib/plugins/codex.js.map +7 -0
- package/dist/cjs/cli/lib/plugins/harness.js +50 -0
- package/dist/cjs/cli/lib/plugins/harness.js.map +7 -0
- package/dist/cjs/cli/lib/plugins/marketplaces.js +76 -0
- package/dist/cjs/cli/lib/plugins/marketplaces.js.map +7 -0
- package/dist/cjs/cli/lib/plugins/skills.js +98 -0
- package/dist/cjs/cli/lib/plugins/skills.js.map +7 -0
- package/dist/cjs/cli/lib/plugins/utils.js +125 -0
- package/dist/cjs/cli/lib/plugins/utils.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/plugins.js +114 -0
- package/dist/cjs/cli/plugins.js.map +7 -0
- package/dist/cjs/cli/program.js +3 -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/server/impl/actions_impl.js.map +2 -2
- 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/accountManagement.d.ts +10 -0
- package/dist/cjs-types/cli/lib/accountManagement.d.ts.map +1 -0
- package/dist/cjs-types/cli/lib/accounts.d.ts +59 -0
- package/dist/cjs-types/cli/lib/accounts.d.ts.map +1 -0
- 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/plugins/claudeCode.d.ts +25 -0
- package/dist/cjs-types/cli/lib/plugins/claudeCode.d.ts.map +1 -0
- package/dist/cjs-types/cli/lib/plugins/codex.d.ts +25 -0
- package/dist/cjs-types/cli/lib/plugins/codex.d.ts.map +1 -0
- package/dist/cjs-types/cli/lib/plugins/harness.d.ts +33 -0
- package/dist/cjs-types/cli/lib/plugins/harness.d.ts.map +1 -0
- package/dist/cjs-types/cli/lib/plugins/marketplaces.d.ts +40 -0
- package/dist/cjs-types/cli/lib/plugins/marketplaces.d.ts.map +1 -0
- package/dist/cjs-types/cli/lib/plugins/skills.d.ts +18 -0
- package/dist/cjs-types/cli/lib/plugins/skills.d.ts.map +1 -0
- package/dist/cjs-types/cli/lib/plugins/utils.d.ts +21 -0
- package/dist/cjs-types/cli/lib/plugins/utils.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/plugins.d.ts +3 -0
- package/dist/cjs-types/cli/plugins.d.ts.map +1 -0
- 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/cjs-types/server/impl/actions_impl.d.ts +3 -2
- package/dist/cjs-types/server/impl/actions_impl.d.ts.map +1 -1
- package/dist/cli.bundle.cjs +10059 -7094
- 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/accountManagement.js +179 -0
- package/dist/esm/cli/lib/accountManagement.js.map +7 -0
- package/dist/esm/cli/lib/accounts.js +289 -0
- package/dist/esm/cli/lib/accounts.js.map +7 -0
- 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 +52 -4
- package/dist/esm/cli/lib/login.js.map +3 -3
- 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/plugins/claudeCode.js +160 -0
- package/dist/esm/cli/lib/plugins/claudeCode.js.map +7 -0
- package/dist/esm/cli/lib/plugins/codex.js +140 -0
- package/dist/esm/cli/lib/plugins/codex.js.map +7 -0
- package/dist/esm/cli/lib/plugins/harness.js +17 -0
- package/dist/esm/cli/lib/plugins/harness.js.map +7 -0
- package/dist/esm/cli/lib/plugins/marketplaces.js +53 -0
- package/dist/esm/cli/lib/plugins/marketplaces.js.map +7 -0
- package/dist/esm/cli/lib/plugins/skills.js +65 -0
- package/dist/esm/cli/lib/plugins/skills.js.map +7 -0
- package/dist/esm/cli/lib/plugins/utils.js +86 -0
- package/dist/esm/cli/lib/plugins/utils.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/plugins.js +99 -0
- package/dist/esm/cli/plugins.js.map +7 -0
- package/dist/esm/cli/program.js +3 -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/server/impl/actions_impl.js.map +2 -2
- 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/accountManagement.d.ts +10 -0
- package/dist/esm-types/cli/lib/accountManagement.d.ts.map +1 -0
- package/dist/esm-types/cli/lib/accounts.d.ts +59 -0
- package/dist/esm-types/cli/lib/accounts.d.ts.map +1 -0
- 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/plugins/claudeCode.d.ts +25 -0
- package/dist/esm-types/cli/lib/plugins/claudeCode.d.ts.map +1 -0
- package/dist/esm-types/cli/lib/plugins/codex.d.ts +25 -0
- package/dist/esm-types/cli/lib/plugins/codex.d.ts.map +1 -0
- package/dist/esm-types/cli/lib/plugins/harness.d.ts +33 -0
- package/dist/esm-types/cli/lib/plugins/harness.d.ts.map +1 -0
- package/dist/esm-types/cli/lib/plugins/marketplaces.d.ts +40 -0
- package/dist/esm-types/cli/lib/plugins/marketplaces.d.ts.map +1 -0
- package/dist/esm-types/cli/lib/plugins/skills.d.ts +18 -0
- package/dist/esm-types/cli/lib/plugins/skills.d.ts.map +1 -0
- package/dist/esm-types/cli/lib/plugins/utils.d.ts +21 -0
- package/dist/esm-types/cli/lib/plugins/utils.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/plugins.d.ts +3 -0
- package/dist/esm-types/cli/plugins.d.ts.map +1 -0
- 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/esm-types/server/impl/actions_impl.d.ts +3 -2
- package/dist/esm-types/server/impl/actions_impl.d.ts.map +1 -1
- package/dist/react.bundle.js +1 -1
- package/dist/react.bundle.js.map +1 -1
- package/package.json +2 -2
- 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/accountManagement.ts +262 -0
- package/src/cli/lib/accounts.ts +400 -0
- 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 +64 -3
- package/src/cli/lib/mcp/requestContext.ts +7 -1
- package/src/cli/lib/plugins/claudeCode.ts +201 -0
- package/src/cli/lib/plugins/codex.ts +177 -0
- package/src/cli/lib/plugins/harness.ts +49 -0
- package/src/cli/lib/plugins/marketplaces.ts +94 -0
- package/src/cli/lib/plugins/skills.ts +101 -0
- package/src/cli/lib/plugins/utils.ts +121 -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/plugins.ts +154 -0
- package/src/cli/program.ts +4 -0
- package/src/index.ts +1 -1
- package/src/server/impl/actions_impl.ts +3 -2
|
@@ -102,7 +102,7 @@ export async function eraseDeploymentEnvVar(ctx: Context): Promise<boolean> {
|
|
|
102
102
|
return true;
|
|
103
103
|
}
|
|
104
104
|
|
|
105
|
-
async function gitIgnoreEnvVarFile(ctx: Context): Promise<boolean> {
|
|
105
|
+
export async function gitIgnoreEnvVarFile(ctx: Context): Promise<boolean> {
|
|
106
106
|
const gitIgnorePath = ".gitignore";
|
|
107
107
|
const gitIgnoreContents = ctx.fs.exists(gitIgnorePath)
|
|
108
108
|
? ctx.fs.readUtf8File(gitIgnorePath)
|
|
@@ -186,7 +186,7 @@ export async function deploymentNameFromAdminKeyOrCrash(
|
|
|
186
186
|
return deploymentName;
|
|
187
187
|
}
|
|
188
188
|
|
|
189
|
-
function deploymentNameFromAdminKey(adminKey: string) {
|
|
189
|
+
export function deploymentNameFromAdminKey(adminKey: string) {
|
|
190
190
|
const parts = adminKey.split("|");
|
|
191
191
|
if (parts.length === 1) {
|
|
192
192
|
return null;
|
|
@@ -31,18 +31,26 @@ import {
|
|
|
31
31
|
targetProjectForLocalSelector,
|
|
32
32
|
} from "./localDeployment/projectMismatch.js";
|
|
33
33
|
import { chalkStderr } from "chalk";
|
|
34
|
-
import {
|
|
34
|
+
import {
|
|
35
|
+
getBuildEnvironment,
|
|
36
|
+
loadProjectEnvironmentVariables,
|
|
37
|
+
} from "./envvars.js";
|
|
35
38
|
import { readGlobalConfig } from "./utils/globalConfig.js";
|
|
39
|
+
import {
|
|
40
|
+
getAccountSelection,
|
|
41
|
+
AccountSelection,
|
|
42
|
+
readAccountAuth,
|
|
43
|
+
} from "./accounts.js";
|
|
36
44
|
import {
|
|
37
45
|
CONVEX_DEPLOYMENT_ENV_VAR_NAME,
|
|
38
46
|
CONVEX_DEPLOYMENT_TOKEN_ENV_VAR_NAME,
|
|
39
47
|
CONVEX_DEPLOY_KEY_ENV_VAR_NAME,
|
|
40
48
|
CONVEX_SELF_HOSTED_ADMIN_KEY_VAR_NAME,
|
|
41
49
|
CONVEX_SELF_HOSTED_URL_VAR_NAME,
|
|
42
|
-
ENV_VAR_FILE_PATH,
|
|
43
50
|
bigBrainAPI,
|
|
44
51
|
processDeployKeyValue,
|
|
45
52
|
readDeployKeyFromEnv,
|
|
53
|
+
readDeployKeyEnvVar,
|
|
46
54
|
typedPlatformClient,
|
|
47
55
|
} from "./utils/utils.js";
|
|
48
56
|
import * as dotenv from "dotenv";
|
|
@@ -83,10 +91,11 @@ export async function initializeBigBrainAuth(
|
|
|
83
91
|
if (initialArgs.url !== undefined && initialArgs.adminKey !== undefined) {
|
|
84
92
|
// Do not check any env vars if `url` and `adminKey` are specified via CLI
|
|
85
93
|
ctx._updateBigBrainAuth(
|
|
86
|
-
getBigBrainAuth(ctx, {
|
|
94
|
+
await getBigBrainAuth(ctx, {
|
|
87
95
|
previewDeployKey: null,
|
|
88
96
|
projectKey: null,
|
|
89
97
|
deploymentKey: null,
|
|
98
|
+
skipAccount: true,
|
|
90
99
|
}),
|
|
91
100
|
);
|
|
92
101
|
return;
|
|
@@ -106,30 +115,31 @@ export async function initializeBigBrainAuth(
|
|
|
106
115
|
const rawDeployKey = readDeployKeyFromEnv((name) => config[name]);
|
|
107
116
|
const deployKey = await processDeployKeyValue(ctx, rawDeployKey);
|
|
108
117
|
if (deployKey !== undefined) {
|
|
109
|
-
const bigBrainAuth = getBigBrainAuth(ctx, {
|
|
118
|
+
const bigBrainAuth = await getBigBrainAuth(ctx, {
|
|
110
119
|
previewDeployKey: isPreviewDeployKey(deployKey) ? deployKey : null,
|
|
111
120
|
projectKey: isProjectKey(deployKey) ? deployKey : null,
|
|
112
121
|
deploymentKey: isDeploymentKey(deployKey) ? deployKey : null,
|
|
122
|
+
envFile: initialArgs.envFile,
|
|
113
123
|
});
|
|
114
124
|
ctx._updateBigBrainAuth(bigBrainAuth);
|
|
115
125
|
return;
|
|
116
126
|
}
|
|
117
127
|
// No deploy key was found in the env file, so fall back on using the global config
|
|
118
128
|
ctx._updateBigBrainAuth(
|
|
119
|
-
getBigBrainAuth(ctx, {
|
|
129
|
+
await getBigBrainAuth(ctx, {
|
|
120
130
|
previewDeployKey: null,
|
|
121
131
|
projectKey: null,
|
|
122
132
|
deploymentKey: null,
|
|
133
|
+
envFile: initialArgs.envFile,
|
|
123
134
|
}),
|
|
124
135
|
);
|
|
125
136
|
return;
|
|
126
137
|
}
|
|
127
|
-
|
|
128
|
-
dotenv.config();
|
|
138
|
+
loadProjectEnvironmentVariables();
|
|
129
139
|
const rawDeployKey = readDeployKeyFromEnv((name) => process.env[name]);
|
|
130
140
|
const deployKey = await processDeployKeyValue(ctx, rawDeployKey);
|
|
131
141
|
if (deployKey !== undefined) {
|
|
132
|
-
const bigBrainAuth = getBigBrainAuth(ctx, {
|
|
142
|
+
const bigBrainAuth = await getBigBrainAuth(ctx, {
|
|
133
143
|
previewDeployKey: isPreviewDeployKey(deployKey) ? deployKey : null,
|
|
134
144
|
projectKey: isProjectKey(deployKey) ? deployKey : null,
|
|
135
145
|
deploymentKey: isDeploymentKey(deployKey) ? deployKey : null,
|
|
@@ -138,7 +148,7 @@ export async function initializeBigBrainAuth(
|
|
|
138
148
|
return;
|
|
139
149
|
}
|
|
140
150
|
ctx._updateBigBrainAuth(
|
|
141
|
-
getBigBrainAuth(ctx, {
|
|
151
|
+
await getBigBrainAuth(ctx, {
|
|
142
152
|
previewDeployKey: null,
|
|
143
153
|
projectKey: null,
|
|
144
154
|
deploymentKey: null,
|
|
@@ -150,6 +160,7 @@ export async function initializeBigBrainAuth(
|
|
|
150
160
|
export async function updateBigBrainAuthAfterLogin(
|
|
151
161
|
ctx: Context,
|
|
152
162
|
accessToken: string,
|
|
163
|
+
account?: AccountSelection,
|
|
153
164
|
) {
|
|
154
165
|
const existingAuth = ctx.bigBrainAuth();
|
|
155
166
|
if (existingAuth !== null && existingAuth.kind === "projectKey") {
|
|
@@ -162,6 +173,7 @@ export async function updateBigBrainAuthAfterLogin(
|
|
|
162
173
|
accessToken: accessToken,
|
|
163
174
|
kind: "accessToken",
|
|
164
175
|
header: `Bearer ${accessToken}`,
|
|
176
|
+
...(account ? { account } : {}),
|
|
165
177
|
});
|
|
166
178
|
}
|
|
167
179
|
|
|
@@ -169,19 +181,22 @@ export async function clearBigBrainAuth(ctx: Context) {
|
|
|
169
181
|
ctx._updateBigBrainAuth(null);
|
|
170
182
|
}
|
|
171
183
|
|
|
172
|
-
function getBigBrainAuth(
|
|
184
|
+
async function getBigBrainAuth(
|
|
173
185
|
ctx: Context,
|
|
174
186
|
opts: {
|
|
175
187
|
previewDeployKey: string | null;
|
|
176
188
|
projectKey: string | null;
|
|
177
189
|
deploymentKey: string | null;
|
|
190
|
+
envFile?: string;
|
|
191
|
+
skipAccount?: boolean;
|
|
178
192
|
},
|
|
179
|
-
): BigBrainAuth | null {
|
|
193
|
+
): Promise<BigBrainAuth | null> {
|
|
180
194
|
if (process.env.CONVEX_OVERRIDE_ACCESS_TOKEN) {
|
|
181
195
|
return {
|
|
182
196
|
accessToken: process.env.CONVEX_OVERRIDE_ACCESS_TOKEN,
|
|
183
197
|
kind: "accessToken",
|
|
184
198
|
header: `Bearer ${process.env.CONVEX_OVERRIDE_ACCESS_TOKEN}`,
|
|
199
|
+
account: null,
|
|
185
200
|
};
|
|
186
201
|
}
|
|
187
202
|
if (opts.projectKey !== null) {
|
|
@@ -200,12 +215,19 @@ function getBigBrainAuth(
|
|
|
200
215
|
deploymentKey: opts.deploymentKey,
|
|
201
216
|
};
|
|
202
217
|
}
|
|
218
|
+
const account = opts.skipAccount
|
|
219
|
+
? null
|
|
220
|
+
: await getAccountSelection(ctx, { envFile: opts.envFile });
|
|
221
|
+
if (account !== null) {
|
|
222
|
+
return await readAccountAuth(ctx, account);
|
|
223
|
+
}
|
|
203
224
|
const globalConfig = readGlobalConfig(ctx);
|
|
204
225
|
if (globalConfig) {
|
|
205
226
|
return {
|
|
206
227
|
kind: "accessToken",
|
|
207
228
|
header: `Bearer ${globalConfig.accessToken}`,
|
|
208
229
|
accessToken: globalConfig.accessToken,
|
|
230
|
+
account: null,
|
|
209
231
|
};
|
|
210
232
|
}
|
|
211
233
|
if (opts.previewDeployKey !== null) {
|
|
@@ -272,9 +294,9 @@ export async function ensureAuthCanCreateDeployment(
|
|
|
272
294
|
)} and try again.`,
|
|
273
295
|
});
|
|
274
296
|
}
|
|
275
|
-
const envVar =
|
|
276
|
-
|
|
277
|
-
|
|
297
|
+
const envVar =
|
|
298
|
+
readDeployKeyEnvVar((name) => process.env[name])?.name ??
|
|
299
|
+
CONVEX_DEPLOY_KEY_ENV_VAR_NAME;
|
|
278
300
|
return await ctx.crash({
|
|
279
301
|
exitCode: 1,
|
|
280
302
|
errorType: "fatal",
|
|
@@ -569,10 +591,7 @@ async function resolveBaseDeploymentSelection(
|
|
|
569
591
|
}
|
|
570
592
|
return result.metadata;
|
|
571
593
|
}
|
|
572
|
-
|
|
573
|
-
dotenv.config({ path: ENV_VAR_FILE_PATH });
|
|
574
|
-
// for variables not already set, use .env values
|
|
575
|
-
dotenv.config();
|
|
594
|
+
loadProjectEnvironmentVariables();
|
|
576
595
|
const result = await getDeploymentSelectionFromEnv(
|
|
577
596
|
ctx,
|
|
578
597
|
selectionWithinProject,
|
package/src/cli/lib/envvars.ts
CHANGED
|
@@ -6,7 +6,49 @@ import * as dotenv from "dotenv";
|
|
|
6
6
|
|
|
7
7
|
import { Context } from "../../bundler/context.js";
|
|
8
8
|
import { logWarning } from "../../bundler/log.js";
|
|
9
|
-
import {
|
|
9
|
+
import {
|
|
10
|
+
CONVEX_DEPLOY_KEY_ENV_VAR_NAME,
|
|
11
|
+
readDeployKeyEnvVar,
|
|
12
|
+
ENV_VAR_FILE_PATH,
|
|
13
|
+
loadPackageJson,
|
|
14
|
+
} from "./utils/utils.js";
|
|
15
|
+
|
|
16
|
+
export const CONVEX_ACCOUNT_ENV_VAR = "CONVEX_ACCOUNT";
|
|
17
|
+
|
|
18
|
+
const loadedEnvironment = new Map<string, { value: string; source: string }>();
|
|
19
|
+
|
|
20
|
+
export function loadProjectEnvironmentVariables() {
|
|
21
|
+
// Only remove values supplied by our previous load; later shell overrides win.
|
|
22
|
+
for (const [name, { value }] of loadedEnvironment) {
|
|
23
|
+
if (process.env[name] === value) delete process.env[name];
|
|
24
|
+
}
|
|
25
|
+
loadedEnvironment.clear();
|
|
26
|
+
const environment: Record<string, string> = {};
|
|
27
|
+
for (const [name, value] of Object.entries(process.env)) {
|
|
28
|
+
if (value !== undefined) environment[name] = value;
|
|
29
|
+
}
|
|
30
|
+
for (const file of [ENV_VAR_FILE_PATH, ".env"]) {
|
|
31
|
+
dotenv.config({ path: file, processEnv: environment });
|
|
32
|
+
for (const [name, value] of Object.entries(environment)) {
|
|
33
|
+
// Account selection reads the current project's files directly.
|
|
34
|
+
if (name !== CONVEX_ACCOUNT_ENV_VAR && process.env[name] === undefined) {
|
|
35
|
+
process.env[name] = value;
|
|
36
|
+
loadedEnvironment.set(name, { value, source: file });
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
export function getDeployKeySource(key: string) {
|
|
43
|
+
const envVarName =
|
|
44
|
+
readDeployKeyEnvVar((name) => process.env[name])?.name ??
|
|
45
|
+
CONVEX_DEPLOY_KEY_ENV_VAR_NAME;
|
|
46
|
+
const loaded = loadedEnvironment.get(envVarName);
|
|
47
|
+
return {
|
|
48
|
+
envVarName,
|
|
49
|
+
source: loaded?.value === key ? loaded.source : "shell environment",
|
|
50
|
+
};
|
|
51
|
+
}
|
|
10
52
|
|
|
11
53
|
const _FRAMEWORKS = [
|
|
12
54
|
"create-react-app",
|
package/src/cli/lib/fsUtils.ts
CHANGED
|
@@ -6,12 +6,15 @@ import { NodeFs } from "../../bundler/fs.js";
|
|
|
6
6
|
export function recursivelyDelete(
|
|
7
7
|
ctx: Context,
|
|
8
8
|
deletePath: string,
|
|
9
|
-
opts?: { force?: boolean; dryRun?: boolean },
|
|
9
|
+
opts?: { force?: boolean; dryRun?: boolean; followSymlinks?: boolean },
|
|
10
10
|
) {
|
|
11
11
|
const dryRun = !!opts?.dryRun;
|
|
12
12
|
let st;
|
|
13
13
|
try {
|
|
14
|
-
st =
|
|
14
|
+
st =
|
|
15
|
+
opts?.followSymlinks === false
|
|
16
|
+
? ctx.fs.lstat(deletePath)
|
|
17
|
+
: ctx.fs.stat(deletePath);
|
|
15
18
|
} catch (err: any) {
|
|
16
19
|
if (err.code === "ENOENT" && opts?.force) {
|
|
17
20
|
return;
|
|
@@ -299,7 +299,7 @@ function renderCopyableCommands(text: string): string {
|
|
|
299
299
|
.map((line) =>
|
|
300
300
|
/^- /.test(line)
|
|
301
301
|
? line.replace(
|
|
302
|
-
/`(npx convex [^`]*)`/g,
|
|
302
|
+
/`(npx convex(?:@[^\s`]+)? [^`]*)`/g,
|
|
303
303
|
(_, command) =>
|
|
304
304
|
`<CodeWithCopyButton text={${JSON.stringify(command)}} />`,
|
|
305
305
|
)
|
package/src/cli/lib/login.ts
CHANGED
|
@@ -20,6 +20,7 @@ import {
|
|
|
20
20
|
logMessage,
|
|
21
21
|
logOutput,
|
|
22
22
|
logVerbose,
|
|
23
|
+
logWarning,
|
|
23
24
|
showSpinner,
|
|
24
25
|
} from "../../bundler/log.js";
|
|
25
26
|
import { Issuer } from "openid-client";
|
|
@@ -32,6 +33,18 @@ import {
|
|
|
32
33
|
modifyGlobalConfig,
|
|
33
34
|
} from "./utils/globalConfig.js";
|
|
34
35
|
import { updateBigBrainAuthAfterLogin } from "./deploymentSelection.js";
|
|
36
|
+
import {
|
|
37
|
+
getAccountSelection,
|
|
38
|
+
AccountSelection,
|
|
39
|
+
accountConfigPath,
|
|
40
|
+
writeAccountCredentials,
|
|
41
|
+
readAccountCredentials,
|
|
42
|
+
} from "./accounts.js";
|
|
43
|
+
import {
|
|
44
|
+
accountApi,
|
|
45
|
+
accountLoginMetadata,
|
|
46
|
+
revokeReplacedAccountToken,
|
|
47
|
+
} from "./accountManagement.js";
|
|
35
48
|
|
|
36
49
|
// Per https://github.com/panva/node-openid-client/tree/main/docs#customizing
|
|
37
50
|
custom.setHttpOptionsDefaults({
|
|
@@ -291,6 +304,7 @@ export async function performLogin(
|
|
|
291
304
|
anonymousId,
|
|
292
305
|
vercel,
|
|
293
306
|
vercelOverride,
|
|
307
|
+
account: selectedAccount,
|
|
294
308
|
}: {
|
|
295
309
|
overrideAuthUrl?: string | undefined;
|
|
296
310
|
overrideAuthClient?: string | undefined;
|
|
@@ -307,8 +321,16 @@ export async function performLogin(
|
|
|
307
321
|
anonymousId?: string | undefined;
|
|
308
322
|
vercel?: boolean | undefined;
|
|
309
323
|
vercelOverride?: string | undefined;
|
|
324
|
+
account?: AccountSelection | null;
|
|
310
325
|
} = {},
|
|
311
326
|
) {
|
|
327
|
+
const auth = ctx.bigBrainAuth();
|
|
328
|
+
const account =
|
|
329
|
+
selectedAccount !== undefined
|
|
330
|
+
? selectedAccount
|
|
331
|
+
: auth?.kind === "accessToken" && auth.account !== undefined
|
|
332
|
+
? auth.account
|
|
333
|
+
: await getAccountSelection(ctx);
|
|
312
334
|
loginFlow = loginFlow || "auto";
|
|
313
335
|
// Get access token from big-brain
|
|
314
336
|
// Default the device name to the hostname, but allow the user to change this if the terminal is interactive.
|
|
@@ -400,6 +422,12 @@ export async function performLogin(
|
|
|
400
422
|
}
|
|
401
423
|
|
|
402
424
|
// Exchange the WorkOS access token for a Convex personal access token.
|
|
425
|
+
const previous =
|
|
426
|
+
account === null ? null : await readAccountCredentials(ctx, account, true);
|
|
427
|
+
const metadata =
|
|
428
|
+
account === null
|
|
429
|
+
? {}
|
|
430
|
+
: await accountLoginMetadata(ctx, account, accessToken, previous);
|
|
403
431
|
ctx._updateBigBrainAuth({
|
|
404
432
|
accessToken: accessToken,
|
|
405
433
|
kind: "accessToken",
|
|
@@ -417,8 +445,34 @@ export async function performLogin(
|
|
|
417
445
|
const newAccessToken = response.data!.accessToken;
|
|
418
446
|
const globalConfig = { accessToken: newAccessToken };
|
|
419
447
|
try {
|
|
420
|
-
|
|
421
|
-
|
|
448
|
+
if (account !== null) {
|
|
449
|
+
await writeAccountCredentials(
|
|
450
|
+
ctx,
|
|
451
|
+
account.name,
|
|
452
|
+
newAccessToken,
|
|
453
|
+
metadata,
|
|
454
|
+
previous,
|
|
455
|
+
async () => {
|
|
456
|
+
try {
|
|
457
|
+
const response = await accountApi(
|
|
458
|
+
newAccessToken,
|
|
459
|
+
"/v1/delete_personal_access_token",
|
|
460
|
+
{ id: newAccessToken },
|
|
461
|
+
);
|
|
462
|
+
if (response.ok) return;
|
|
463
|
+
} catch {
|
|
464
|
+
// Report cleanup failure alongside the account conflict below.
|
|
465
|
+
}
|
|
466
|
+
logWarning(
|
|
467
|
+
"The unused login token could not be revoked. You can remove it in the dashboard's account settings.",
|
|
468
|
+
);
|
|
469
|
+
},
|
|
470
|
+
);
|
|
471
|
+
} else {
|
|
472
|
+
await modifyGlobalConfig(ctx, globalConfig);
|
|
473
|
+
}
|
|
474
|
+
const path =
|
|
475
|
+
account !== null ? accountConfigPath(account.name) : globalConfigPath();
|
|
422
476
|
logFinishedStep(`Saved credentials to ${formatPathForPrinting(path)}`);
|
|
423
477
|
} catch (err: unknown) {
|
|
424
478
|
return await ctx.crash({
|
|
@@ -429,8 +483,15 @@ export async function performLogin(
|
|
|
429
483
|
});
|
|
430
484
|
}
|
|
431
485
|
|
|
486
|
+
if (account !== null) {
|
|
487
|
+
await revokeReplacedAccountToken(
|
|
488
|
+
ctx,
|
|
489
|
+
previous?.accessToken,
|
|
490
|
+
newAccessToken,
|
|
491
|
+
);
|
|
492
|
+
}
|
|
432
493
|
logVerbose(`performLogin: updating big brain auth after login`);
|
|
433
|
-
await updateBigBrainAuthAfterLogin(ctx, newAccessToken);
|
|
494
|
+
await updateBigBrainAuthAfterLogin(ctx, newAccessToken, account ?? undefined);
|
|
434
495
|
|
|
435
496
|
logVerbose(`performLogin: checking opt ins, acceptOptIns: ${acceptOptIns}`);
|
|
436
497
|
// Do opt in to TOS and Privacy Policy stuff
|
|
@@ -149,7 +149,13 @@ export function encodeDeploymentSelector(
|
|
|
149
149
|
projectDir,
|
|
150
150
|
deployment,
|
|
151
151
|
};
|
|
152
|
-
|
|
152
|
+
// ASCII JSON escapes support Unicode paths with the existing Latin-1 decoder.
|
|
153
|
+
const serialized = JSON.stringify(payload).replace(
|
|
154
|
+
/[\u0080-\uffff]/g,
|
|
155
|
+
(character) =>
|
|
156
|
+
`\\u${character.charCodeAt(0).toString(16).padStart(4, "0")}`,
|
|
157
|
+
);
|
|
158
|
+
return `${deployment.kind}:${btoa(serialized)}`;
|
|
153
159
|
}
|
|
154
160
|
|
|
155
161
|
const payloadSchema = z.object({
|
|
@@ -0,0 +1,201 @@
|
|
|
1
|
+
import os from "os";
|
|
2
|
+
import path from "path";
|
|
3
|
+
import * as Sentry from "@sentry/node";
|
|
4
|
+
import { z } from "zod";
|
|
5
|
+
import type { Cli, Places } from "./harness.js";
|
|
6
|
+
import {
|
|
7
|
+
convexMarketplaces,
|
|
8
|
+
type Marketplace,
|
|
9
|
+
type Plugin,
|
|
10
|
+
type Source,
|
|
11
|
+
} from "./marketplaces.js";
|
|
12
|
+
import {
|
|
13
|
+
fetchGitHubJson,
|
|
14
|
+
onPath,
|
|
15
|
+
parseList,
|
|
16
|
+
subfolders,
|
|
17
|
+
versionOf,
|
|
18
|
+
} from "./utils.js";
|
|
19
|
+
|
|
20
|
+
/** Builds `getClaudeCodeLocations` for the given places, so tests can point it at fixtures. */
|
|
21
|
+
export const createGetClaudeCodeLocations =
|
|
22
|
+
(places: Omit<Places, "applications">) => async (): Promise<Cli[]> => {
|
|
23
|
+
const found: Omit<Cli, "version">[] = [
|
|
24
|
+
...onPath("claude", places.path).map((p) => ({
|
|
25
|
+
source: "terminal" as const,
|
|
26
|
+
path: p,
|
|
27
|
+
})),
|
|
28
|
+
// The desktop app keeps every version it downloaded, as
|
|
29
|
+
// `<version>/<hash>/claude.app`, next to its own state files.
|
|
30
|
+
...subfolders(
|
|
31
|
+
path.join(
|
|
32
|
+
places.home,
|
|
33
|
+
"Library",
|
|
34
|
+
"Application Support",
|
|
35
|
+
"Claude",
|
|
36
|
+
"claude-code",
|
|
37
|
+
),
|
|
38
|
+
)
|
|
39
|
+
.flatMap(subfolders)
|
|
40
|
+
.map((dir) => ({
|
|
41
|
+
source: "desktop" as const,
|
|
42
|
+
path: path.join(dir, "claude.app", "Contents", "MacOS", "claude"),
|
|
43
|
+
})),
|
|
44
|
+
...subfolders(path.join(places.home, ".vscode", "extensions"))
|
|
45
|
+
.filter((dir) =>
|
|
46
|
+
path.basename(dir).startsWith("anthropic.claude-code-"),
|
|
47
|
+
)
|
|
48
|
+
.map((dir) => ({
|
|
49
|
+
source: "vscode" as const,
|
|
50
|
+
path: path.join(dir, "resources", "native-binary", "claude"),
|
|
51
|
+
})),
|
|
52
|
+
];
|
|
53
|
+
return Promise.all(
|
|
54
|
+
found.map(async (cli) => ({
|
|
55
|
+
...cli,
|
|
56
|
+
version: await versionOf(cli.path),
|
|
57
|
+
})),
|
|
58
|
+
);
|
|
59
|
+
};
|
|
60
|
+
|
|
61
|
+
/** Every copy of Claude Code on this machine: the terminal CLI, the Claude desktop app's copies and the VS Code extension's copies. */
|
|
62
|
+
export const getClaudeCodeLocations = createGetClaudeCodeLocations({
|
|
63
|
+
home: os.homedir(),
|
|
64
|
+
path: process.env.PATH ?? "",
|
|
65
|
+
});
|
|
66
|
+
|
|
67
|
+
/** The newest Convex plugin a Claude Code user can install, which is the commit Anthropic's marketplace pins, not our main. */
|
|
68
|
+
export async function claudeCodeLatest(): Promise<Record<string, string>> {
|
|
69
|
+
const marketplace = await fetchGitHubJson<{
|
|
70
|
+
plugins: { name: string; source: { url: string; sha: string } }[];
|
|
71
|
+
}>("anthropics/claude-plugins-official/main/.claude-plugin/marketplace.json");
|
|
72
|
+
if (marketplace === null) return {};
|
|
73
|
+
const source = marketplace.plugins.find((p) => p.name === "convex")?.source;
|
|
74
|
+
if (source === undefined) {
|
|
75
|
+
Sentry.captureMessage(
|
|
76
|
+
"Anthropic's marketplace has no convex plugin",
|
|
77
|
+
"error",
|
|
78
|
+
);
|
|
79
|
+
return {};
|
|
80
|
+
}
|
|
81
|
+
const repo = source.url
|
|
82
|
+
.replace(/^https:\/\/github\.com\//, "")
|
|
83
|
+
.replace(/\.git$/, "");
|
|
84
|
+
const plugin = await fetchGitHubJson<{ version: string }>(
|
|
85
|
+
`${repo}/${source.sha}/.claude-plugin/plugin.json`,
|
|
86
|
+
);
|
|
87
|
+
return plugin === null ? {} : { convex: plugin.version };
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
/** `claude plugin marketplace list --json` */
|
|
91
|
+
const claudeCodeMarketplaceList = z.array(
|
|
92
|
+
z.union([
|
|
93
|
+
z.object({
|
|
94
|
+
name: z.string(),
|
|
95
|
+
source: z.literal("github"),
|
|
96
|
+
repo: z.string(),
|
|
97
|
+
}),
|
|
98
|
+
z.object({ name: z.string(), source: z.literal("git"), url: z.string() }),
|
|
99
|
+
z.object({
|
|
100
|
+
name: z.string(),
|
|
101
|
+
source: z.literal("directory"),
|
|
102
|
+
path: z.string(),
|
|
103
|
+
}),
|
|
104
|
+
// The list holds other people's marketplaces too, so a source type we
|
|
105
|
+
// haven't seen is kept by its type name instead of rejecting the list.
|
|
106
|
+
z.object({ name: z.string(), source: z.string() }),
|
|
107
|
+
]),
|
|
108
|
+
);
|
|
109
|
+
|
|
110
|
+
/** Where a Claude Code marketplace comes from. zod keeps only the fields of the shape that matched. */
|
|
111
|
+
function claudeCodeSource(
|
|
112
|
+
m: z.infer<typeof claudeCodeMarketplaceList>[number],
|
|
113
|
+
): Source {
|
|
114
|
+
if ("repo" in m) return { kind: "github", repo: m.repo };
|
|
115
|
+
if ("url" in m) return { kind: "git", url: m.url };
|
|
116
|
+
if ("path" in m) return { kind: "folder", path: m.path };
|
|
117
|
+
return { kind: "other", type: m.source };
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
/** `claude plugin list --json` */
|
|
121
|
+
const claudeCodePluginList = z.array(
|
|
122
|
+
z.object({
|
|
123
|
+
id: z.string(),
|
|
124
|
+
version: z.string(),
|
|
125
|
+
scope: z.string(),
|
|
126
|
+
enabled: z.boolean(),
|
|
127
|
+
folderVersion: z.string().optional(),
|
|
128
|
+
}),
|
|
129
|
+
);
|
|
130
|
+
|
|
131
|
+
/** Reads the plugins in `claude plugin list --json`, or returns null when it doesn't match. */
|
|
132
|
+
function claudeCodePlugins(pluginList: string): Plugin[] | null {
|
|
133
|
+
const plugins = parseList(
|
|
134
|
+
claudeCodePluginList,
|
|
135
|
+
pluginList,
|
|
136
|
+
"claude plugin list --json",
|
|
137
|
+
);
|
|
138
|
+
if (plugins === null) return null;
|
|
139
|
+
return plugins.map((p) => {
|
|
140
|
+
const [name, marketplace] = p.id.split("@");
|
|
141
|
+
// A folder install runs from the folder, and `version` is the copy cached at install time.
|
|
142
|
+
return {
|
|
143
|
+
name,
|
|
144
|
+
marketplace,
|
|
145
|
+
version: p.folderVersion ?? p.version,
|
|
146
|
+
scope: p.scope,
|
|
147
|
+
enabled: p.enabled,
|
|
148
|
+
cachedVersion:
|
|
149
|
+
p.folderVersion !== undefined && p.folderVersion !== p.version
|
|
150
|
+
? p.version
|
|
151
|
+
: undefined,
|
|
152
|
+
};
|
|
153
|
+
});
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
/** Reads Claude Code's Convex marketplaces from `claude plugin marketplace list --json` and `claude plugin list --json`, or returns null when either doesn't match. */
|
|
157
|
+
export function claudeCodeMarketplaces(
|
|
158
|
+
marketplaceList: string,
|
|
159
|
+
pluginList: string,
|
|
160
|
+
): Marketplace[] | null {
|
|
161
|
+
const marketplaces = parseList(
|
|
162
|
+
claudeCodeMarketplaceList,
|
|
163
|
+
marketplaceList,
|
|
164
|
+
"claude plugin marketplace list --json",
|
|
165
|
+
);
|
|
166
|
+
const plugins = claudeCodePlugins(pluginList);
|
|
167
|
+
if (marketplaces === null || plugins === null) return null;
|
|
168
|
+
return convexMarketplaces(
|
|
169
|
+
marketplaces.map((m) => ({ name: m.name, source: claudeCodeSource(m) })),
|
|
170
|
+
plugins,
|
|
171
|
+
);
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
const pluginId = "convex@claude-plugins-official";
|
|
175
|
+
/** The install step that brings an installed plugin to the latest version. When it fails, the user keeps an old version. */
|
|
176
|
+
const upgradeStep = ["plugin", "update", pluginId, "--scope", "user"];
|
|
177
|
+
|
|
178
|
+
/** Claude Code as `npx convex plugins` installs and reads it. */
|
|
179
|
+
export const claudeCode = {
|
|
180
|
+
id: "claudeCode" as const,
|
|
181
|
+
name: "Claude Code",
|
|
182
|
+
plugin: pluginId,
|
|
183
|
+
upgradeStep,
|
|
184
|
+
installSteps: [
|
|
185
|
+
// Notes:
|
|
186
|
+
//
|
|
187
|
+
// 1. Claude Code updates its local copy of the marketplace when it
|
|
188
|
+
// installs, but `plugin install` skips a plugin that's already
|
|
189
|
+
// installed, so an old version stays until the plugin is updated.
|
|
190
|
+
|
|
191
|
+
// Add the marketplace
|
|
192
|
+
["plugin", "marketplace", "add", "anthropics/claude-plugins-official"],
|
|
193
|
+
// Install the plugin if it isn't installed yet
|
|
194
|
+
["plugin", "install", pluginId, "--scope", "user"],
|
|
195
|
+
// Update an installed plugin to the latest version
|
|
196
|
+
upgradeStep,
|
|
197
|
+
],
|
|
198
|
+
plugins: claudeCodePlugins,
|
|
199
|
+
marketplaces: claudeCodeMarketplaces,
|
|
200
|
+
latest: claudeCodeLatest,
|
|
201
|
+
};
|