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.
Files changed (248) hide show
  1. package/CHANGELOG.md +7 -0
  2. package/dist/browser.bundle.js +1 -1
  3. package/dist/browser.bundle.js.map +1 -1
  4. package/dist/cjs/bundler/context.js +7 -5
  5. package/dist/cjs/bundler/context.js.map +2 -2
  6. package/dist/cjs/bundler/fs.js +47 -6
  7. package/dist/cjs/bundler/fs.js.map +2 -2
  8. package/dist/cjs/cli/account.js +237 -0
  9. package/dist/cjs/cli/account.js.map +7 -0
  10. package/dist/cjs/cli/configure.js +15 -2
  11. package/dist/cjs/cli/configure.js.map +2 -2
  12. package/dist/cjs/cli/lib/accountManagement.js +196 -0
  13. package/dist/cjs/cli/lib/accountManagement.js.map +7 -0
  14. package/dist/cjs/cli/lib/accounts.js +334 -0
  15. package/dist/cjs/cli/lib/accounts.js.map +7 -0
  16. package/dist/cjs/cli/lib/deployKeyWarning.js +43 -0
  17. package/dist/cjs/cli/lib/deployKeyWarning.js.map +7 -0
  18. package/dist/cjs/cli/lib/deployment.js +2 -0
  19. package/dist/cjs/cli/lib/deployment.js.map +2 -2
  20. package/dist/cjs/cli/lib/deploymentSelection.js +27 -18
  21. package/dist/cjs/cli/lib/deploymentSelection.js.map +2 -2
  22. package/dist/cjs/cli/lib/envvars.js +32 -0
  23. package/dist/cjs/cli/lib/envvars.js.map +2 -2
  24. package/dist/cjs/cli/lib/fsUtils.js +1 -1
  25. package/dist/cjs/cli/lib/fsUtils.js.map +2 -2
  26. package/dist/cjs/cli/lib/generateDocs.js +1 -1
  27. package/dist/cjs/cli/lib/generateDocs.js.map +1 -1
  28. package/dist/cjs/cli/lib/login.js +42 -4
  29. package/dist/cjs/cli/lib/login.js.map +3 -3
  30. package/dist/cjs/cli/lib/mcp/requestContext.js +5 -1
  31. package/dist/cjs/cli/lib/mcp/requestContext.js.map +2 -2
  32. package/dist/cjs/cli/lib/plugins/claudeCode.js +188 -0
  33. package/dist/cjs/cli/lib/plugins/claudeCode.js.map +7 -0
  34. package/dist/cjs/cli/lib/plugins/codex.js +167 -0
  35. package/dist/cjs/cli/lib/plugins/codex.js.map +7 -0
  36. package/dist/cjs/cli/lib/plugins/harness.js +50 -0
  37. package/dist/cjs/cli/lib/plugins/harness.js.map +7 -0
  38. package/dist/cjs/cli/lib/plugins/marketplaces.js +76 -0
  39. package/dist/cjs/cli/lib/plugins/marketplaces.js.map +7 -0
  40. package/dist/cjs/cli/lib/plugins/skills.js +98 -0
  41. package/dist/cjs/cli/lib/plugins/skills.js.map +7 -0
  42. package/dist/cjs/cli/lib/plugins/utils.js +125 -0
  43. package/dist/cjs/cli/lib/plugins/utils.js.map +7 -0
  44. package/dist/cjs/cli/lib/utils/utils.js +18 -9
  45. package/dist/cjs/cli/lib/utils/utils.js.map +2 -2
  46. package/dist/cjs/cli/login.js +213 -114
  47. package/dist/cjs/cli/login.js.map +3 -3
  48. package/dist/cjs/cli/logout.js +113 -12
  49. package/dist/cjs/cli/logout.js.map +3 -3
  50. package/dist/cjs/cli/mcp.js +43 -10
  51. package/dist/cjs/cli/mcp.js.map +3 -3
  52. package/dist/cjs/cli/plugins.js +114 -0
  53. package/dist/cjs/cli/plugins.js.map +7 -0
  54. package/dist/cjs/cli/program.js +3 -1
  55. package/dist/cjs/cli/program.js.map +2 -2
  56. package/dist/cjs/index.js +1 -1
  57. package/dist/cjs/index.js.map +1 -1
  58. package/dist/cjs/server/impl/actions_impl.js.map +2 -2
  59. package/dist/cjs-types/bundler/context.d.ts +5 -0
  60. package/dist/cjs-types/bundler/context.d.ts.map +1 -1
  61. package/dist/cjs-types/bundler/fs.d.ts +7 -2
  62. package/dist/cjs-types/bundler/fs.d.ts.map +1 -1
  63. package/dist/cjs-types/cli/account.d.ts +6 -0
  64. package/dist/cjs-types/cli/account.d.ts.map +1 -0
  65. package/dist/cjs-types/cli/configure.d.ts.map +1 -1
  66. package/dist/cjs-types/cli/lib/accountManagement.d.ts +10 -0
  67. package/dist/cjs-types/cli/lib/accountManagement.d.ts.map +1 -0
  68. package/dist/cjs-types/cli/lib/accounts.d.ts +59 -0
  69. package/dist/cjs-types/cli/lib/accounts.d.ts.map +1 -0
  70. package/dist/cjs-types/cli/lib/deployKeyWarning.d.ts +3 -0
  71. package/dist/cjs-types/cli/lib/deployKeyWarning.d.ts.map +1 -0
  72. package/dist/cjs-types/cli/lib/deployment.d.ts +2 -0
  73. package/dist/cjs-types/cli/lib/deployment.d.ts.map +1 -1
  74. package/dist/cjs-types/cli/lib/deploymentSelection.d.ts +2 -1
  75. package/dist/cjs-types/cli/lib/deploymentSelection.d.ts.map +1 -1
  76. package/dist/cjs-types/cli/lib/envvars.d.ts +6 -0
  77. package/dist/cjs-types/cli/lib/envvars.d.ts.map +1 -1
  78. package/dist/cjs-types/cli/lib/fsUtils.d.ts +1 -0
  79. package/dist/cjs-types/cli/lib/fsUtils.d.ts.map +1 -1
  80. package/dist/cjs-types/cli/lib/login.d.ts +3 -1
  81. package/dist/cjs-types/cli/lib/login.d.ts.map +1 -1
  82. package/dist/cjs-types/cli/lib/mcp/requestContext.d.ts.map +1 -1
  83. package/dist/cjs-types/cli/lib/mcp/tools/insights.d.ts +2 -2
  84. package/dist/cjs-types/cli/lib/plugins/claudeCode.d.ts +25 -0
  85. package/dist/cjs-types/cli/lib/plugins/claudeCode.d.ts.map +1 -0
  86. package/dist/cjs-types/cli/lib/plugins/codex.d.ts +25 -0
  87. package/dist/cjs-types/cli/lib/plugins/codex.d.ts.map +1 -0
  88. package/dist/cjs-types/cli/lib/plugins/harness.d.ts +33 -0
  89. package/dist/cjs-types/cli/lib/plugins/harness.d.ts.map +1 -0
  90. package/dist/cjs-types/cli/lib/plugins/marketplaces.d.ts +40 -0
  91. package/dist/cjs-types/cli/lib/plugins/marketplaces.d.ts.map +1 -0
  92. package/dist/cjs-types/cli/lib/plugins/skills.d.ts +18 -0
  93. package/dist/cjs-types/cli/lib/plugins/skills.d.ts.map +1 -0
  94. package/dist/cjs-types/cli/lib/plugins/utils.d.ts +21 -0
  95. package/dist/cjs-types/cli/lib/plugins/utils.d.ts.map +1 -0
  96. package/dist/cjs-types/cli/lib/utils/utils.d.ts +5 -0
  97. package/dist/cjs-types/cli/lib/utils/utils.d.ts.map +1 -1
  98. package/dist/cjs-types/cli/login.d.ts +21 -0
  99. package/dist/cjs-types/cli/login.d.ts.map +1 -1
  100. package/dist/cjs-types/cli/logout.d.ts +8 -1
  101. package/dist/cjs-types/cli/logout.d.ts.map +1 -1
  102. package/dist/cjs-types/cli/mcp.d.ts.map +1 -1
  103. package/dist/cjs-types/cli/plugins.d.ts +3 -0
  104. package/dist/cjs-types/cli/plugins.d.ts.map +1 -0
  105. package/dist/cjs-types/cli/program.d.ts.map +1 -1
  106. package/dist/cjs-types/index.d.ts +1 -1
  107. package/dist/cjs-types/index.d.ts.map +1 -1
  108. package/dist/cjs-types/server/impl/actions_impl.d.ts +3 -2
  109. package/dist/cjs-types/server/impl/actions_impl.d.ts.map +1 -1
  110. package/dist/cli.bundle.cjs +10059 -7094
  111. package/dist/cli.bundle.cjs.map +4 -4
  112. package/dist/esm/bundler/context.js +7 -5
  113. package/dist/esm/bundler/context.js.map +2 -2
  114. package/dist/esm/bundler/fs.js +47 -6
  115. package/dist/esm/bundler/fs.js.map +2 -2
  116. package/dist/esm/cli/account.js +221 -0
  117. package/dist/esm/cli/account.js.map +7 -0
  118. package/dist/esm/cli/configure.js +15 -2
  119. package/dist/esm/cli/configure.js.map +2 -2
  120. package/dist/esm/cli/lib/accountManagement.js +179 -0
  121. package/dist/esm/cli/lib/accountManagement.js.map +7 -0
  122. package/dist/esm/cli/lib/accounts.js +289 -0
  123. package/dist/esm/cli/lib/accounts.js.map +7 -0
  124. package/dist/esm/cli/lib/deployKeyWarning.js +27 -0
  125. package/dist/esm/cli/lib/deployKeyWarning.js.map +7 -0
  126. package/dist/esm/cli/lib/deployment.js +2 -2
  127. package/dist/esm/cli/lib/deployment.js.map +2 -2
  128. package/dist/esm/cli/lib/deploymentSelection.js +35 -20
  129. package/dist/esm/cli/lib/deploymentSelection.js.map +2 -2
  130. package/dist/esm/cli/lib/envvars.js +35 -1
  131. package/dist/esm/cli/lib/envvars.js.map +2 -2
  132. package/dist/esm/cli/lib/fsUtils.js +1 -1
  133. package/dist/esm/cli/lib/fsUtils.js.map +2 -2
  134. package/dist/esm/cli/lib/generateDocs.js +1 -1
  135. package/dist/esm/cli/lib/generateDocs.js.map +1 -1
  136. package/dist/esm/cli/lib/login.js +52 -4
  137. package/dist/esm/cli/lib/login.js.map +3 -3
  138. package/dist/esm/cli/lib/mcp/requestContext.js +5 -1
  139. package/dist/esm/cli/lib/mcp/requestContext.js.map +2 -2
  140. package/dist/esm/cli/lib/plugins/claudeCode.js +160 -0
  141. package/dist/esm/cli/lib/plugins/claudeCode.js.map +7 -0
  142. package/dist/esm/cli/lib/plugins/codex.js +140 -0
  143. package/dist/esm/cli/lib/plugins/codex.js.map +7 -0
  144. package/dist/esm/cli/lib/plugins/harness.js +17 -0
  145. package/dist/esm/cli/lib/plugins/harness.js.map +7 -0
  146. package/dist/esm/cli/lib/plugins/marketplaces.js +53 -0
  147. package/dist/esm/cli/lib/plugins/marketplaces.js.map +7 -0
  148. package/dist/esm/cli/lib/plugins/skills.js +65 -0
  149. package/dist/esm/cli/lib/plugins/skills.js.map +7 -0
  150. package/dist/esm/cli/lib/plugins/utils.js +86 -0
  151. package/dist/esm/cli/lib/plugins/utils.js.map +7 -0
  152. package/dist/esm/cli/lib/utils/utils.js +16 -9
  153. package/dist/esm/cli/lib/utils/utils.js.map +2 -2
  154. package/dist/esm/cli/login.js +225 -112
  155. package/dist/esm/cli/login.js.map +3 -3
  156. package/dist/esm/cli/logout.js +110 -13
  157. package/dist/esm/cli/logout.js.map +2 -2
  158. package/dist/esm/cli/mcp.js +34 -10
  159. package/dist/esm/cli/mcp.js.map +2 -2
  160. package/dist/esm/cli/plugins.js +99 -0
  161. package/dist/esm/cli/plugins.js.map +7 -0
  162. package/dist/esm/cli/program.js +3 -1
  163. package/dist/esm/cli/program.js.map +2 -2
  164. package/dist/esm/index.js +1 -1
  165. package/dist/esm/index.js.map +1 -1
  166. package/dist/esm/server/impl/actions_impl.js.map +2 -2
  167. package/dist/esm-types/bundler/context.d.ts +5 -0
  168. package/dist/esm-types/bundler/context.d.ts.map +1 -1
  169. package/dist/esm-types/bundler/fs.d.ts +7 -2
  170. package/dist/esm-types/bundler/fs.d.ts.map +1 -1
  171. package/dist/esm-types/cli/account.d.ts +6 -0
  172. package/dist/esm-types/cli/account.d.ts.map +1 -0
  173. package/dist/esm-types/cli/configure.d.ts.map +1 -1
  174. package/dist/esm-types/cli/lib/accountManagement.d.ts +10 -0
  175. package/dist/esm-types/cli/lib/accountManagement.d.ts.map +1 -0
  176. package/dist/esm-types/cli/lib/accounts.d.ts +59 -0
  177. package/dist/esm-types/cli/lib/accounts.d.ts.map +1 -0
  178. package/dist/esm-types/cli/lib/deployKeyWarning.d.ts +3 -0
  179. package/dist/esm-types/cli/lib/deployKeyWarning.d.ts.map +1 -0
  180. package/dist/esm-types/cli/lib/deployment.d.ts +2 -0
  181. package/dist/esm-types/cli/lib/deployment.d.ts.map +1 -1
  182. package/dist/esm-types/cli/lib/deploymentSelection.d.ts +2 -1
  183. package/dist/esm-types/cli/lib/deploymentSelection.d.ts.map +1 -1
  184. package/dist/esm-types/cli/lib/envvars.d.ts +6 -0
  185. package/dist/esm-types/cli/lib/envvars.d.ts.map +1 -1
  186. package/dist/esm-types/cli/lib/fsUtils.d.ts +1 -0
  187. package/dist/esm-types/cli/lib/fsUtils.d.ts.map +1 -1
  188. package/dist/esm-types/cli/lib/login.d.ts +3 -1
  189. package/dist/esm-types/cli/lib/login.d.ts.map +1 -1
  190. package/dist/esm-types/cli/lib/mcp/requestContext.d.ts.map +1 -1
  191. package/dist/esm-types/cli/lib/mcp/tools/insights.d.ts +2 -2
  192. package/dist/esm-types/cli/lib/plugins/claudeCode.d.ts +25 -0
  193. package/dist/esm-types/cli/lib/plugins/claudeCode.d.ts.map +1 -0
  194. package/dist/esm-types/cli/lib/plugins/codex.d.ts +25 -0
  195. package/dist/esm-types/cli/lib/plugins/codex.d.ts.map +1 -0
  196. package/dist/esm-types/cli/lib/plugins/harness.d.ts +33 -0
  197. package/dist/esm-types/cli/lib/plugins/harness.d.ts.map +1 -0
  198. package/dist/esm-types/cli/lib/plugins/marketplaces.d.ts +40 -0
  199. package/dist/esm-types/cli/lib/plugins/marketplaces.d.ts.map +1 -0
  200. package/dist/esm-types/cli/lib/plugins/skills.d.ts +18 -0
  201. package/dist/esm-types/cli/lib/plugins/skills.d.ts.map +1 -0
  202. package/dist/esm-types/cli/lib/plugins/utils.d.ts +21 -0
  203. package/dist/esm-types/cli/lib/plugins/utils.d.ts.map +1 -0
  204. package/dist/esm-types/cli/lib/utils/utils.d.ts +5 -0
  205. package/dist/esm-types/cli/lib/utils/utils.d.ts.map +1 -1
  206. package/dist/esm-types/cli/login.d.ts +21 -0
  207. package/dist/esm-types/cli/login.d.ts.map +1 -1
  208. package/dist/esm-types/cli/logout.d.ts +8 -1
  209. package/dist/esm-types/cli/logout.d.ts.map +1 -1
  210. package/dist/esm-types/cli/mcp.d.ts.map +1 -1
  211. package/dist/esm-types/cli/plugins.d.ts +3 -0
  212. package/dist/esm-types/cli/plugins.d.ts.map +1 -0
  213. package/dist/esm-types/cli/program.d.ts.map +1 -1
  214. package/dist/esm-types/index.d.ts +1 -1
  215. package/dist/esm-types/index.d.ts.map +1 -1
  216. package/dist/esm-types/server/impl/actions_impl.d.ts +3 -2
  217. package/dist/esm-types/server/impl/actions_impl.d.ts.map +1 -1
  218. package/dist/react.bundle.js +1 -1
  219. package/dist/react.bundle.js.map +1 -1
  220. package/package.json +2 -2
  221. package/src/bundler/context.ts +9 -5
  222. package/src/bundler/fs.ts +54 -5
  223. package/src/cli/account.ts +320 -0
  224. package/src/cli/configure.ts +18 -2
  225. package/src/cli/lib/accountManagement.ts +262 -0
  226. package/src/cli/lib/accounts.ts +400 -0
  227. package/src/cli/lib/deployKeyWarning.ts +43 -0
  228. package/src/cli/lib/deployment.ts +2 -2
  229. package/src/cli/lib/deploymentSelection.ts +37 -18
  230. package/src/cli/lib/envvars.ts +43 -1
  231. package/src/cli/lib/fsUtils.ts +5 -2
  232. package/src/cli/lib/generateDocs.ts +1 -1
  233. package/src/cli/lib/login.ts +64 -3
  234. package/src/cli/lib/mcp/requestContext.ts +7 -1
  235. package/src/cli/lib/plugins/claudeCode.ts +201 -0
  236. package/src/cli/lib/plugins/codex.ts +177 -0
  237. package/src/cli/lib/plugins/harness.ts +49 -0
  238. package/src/cli/lib/plugins/marketplaces.ts +94 -0
  239. package/src/cli/lib/plugins/skills.ts +101 -0
  240. package/src/cli/lib/plugins/utils.ts +121 -0
  241. package/src/cli/lib/utils/utils.ts +19 -15
  242. package/src/cli/login.ts +293 -168
  243. package/src/cli/logout.ts +142 -17
  244. package/src/cli/mcp.ts +48 -11
  245. package/src/cli/plugins.ts +154 -0
  246. package/src/cli/program.ts +4 -0
  247. package/src/index.ts +1 -1
  248. package/src/server/impl/actions_impl.ts +3 -2
@@ -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,17 @@ import {
32
33
  modifyGlobalConfig
33
34
  } from "./utils/globalConfig.js";
34
35
  import { updateBigBrainAuthAfterLogin } from "./deploymentSelection.js";
36
+ import {
37
+ getAccountSelection,
38
+ accountConfigPath,
39
+ writeAccountCredentials,
40
+ readAccountCredentials
41
+ } from "./accounts.js";
42
+ import {
43
+ accountApi,
44
+ accountLoginMetadata,
45
+ revokeReplacedAccountToken
46
+ } from "./accountManagement.js";
35
47
  custom.setHttpOptionsDefaults({
36
48
  timeout: parseInt(process.env.OPENID_CLIENT_TIMEOUT || "10000")
37
49
  });
@@ -198,8 +210,11 @@ export async function performLogin(ctx, {
198
210
  deviceName: deviceNameOverride,
199
211
  anonymousId,
200
212
  vercel,
201
- vercelOverride
213
+ vercelOverride,
214
+ account: selectedAccount
202
215
  } = {}) {
216
+ const auth = ctx.bigBrainAuth();
217
+ const account = selectedAccount !== void 0 ? selectedAccount : auth?.kind === "accessToken" && auth.account !== void 0 ? auth.account : await getAccountSelection(ctx);
203
218
  loginFlow = loginFlow || "auto";
204
219
  let deviceName = deviceNameOverride ?? "";
205
220
  if (!deviceName && process.platform === "darwin") {
@@ -272,6 +287,8 @@ export async function performLogin(ctx, {
272
287
  printedMessage: null
273
288
  });
274
289
  }
290
+ const previous = account === null ? null : await readAccountCredentials(ctx, account, true);
291
+ const metadata = account === null ? {} : await accountLoginMetadata(ctx, account, accessToken, previous);
275
292
  ctx._updateBigBrainAuth({
276
293
  accessToken,
277
294
  kind: "accessToken",
@@ -289,8 +306,32 @@ export async function performLogin(ctx, {
289
306
  const newAccessToken = response.data.accessToken;
290
307
  const globalConfig = { accessToken: newAccessToken };
291
308
  try {
292
- await modifyGlobalConfig(ctx, globalConfig);
293
- const path = globalConfigPath();
309
+ if (account !== null) {
310
+ await writeAccountCredentials(
311
+ ctx,
312
+ account.name,
313
+ newAccessToken,
314
+ metadata,
315
+ previous,
316
+ async () => {
317
+ try {
318
+ const response2 = await accountApi(
319
+ newAccessToken,
320
+ "/v1/delete_personal_access_token",
321
+ { id: newAccessToken }
322
+ );
323
+ if (response2.ok) return;
324
+ } catch {
325
+ }
326
+ logWarning(
327
+ "The unused login token could not be revoked. You can remove it in the dashboard's account settings."
328
+ );
329
+ }
330
+ );
331
+ } else {
332
+ await modifyGlobalConfig(ctx, globalConfig);
333
+ }
334
+ const path = account !== null ? accountConfigPath(account.name) : globalConfigPath();
294
335
  logFinishedStep(`Saved credentials to ${formatPathForPrinting(path)}`);
295
336
  } catch (err) {
296
337
  return await ctx.crash({
@@ -300,8 +341,15 @@ export async function performLogin(ctx, {
300
341
  printedMessage: null
301
342
  });
302
343
  }
344
+ if (account !== null) {
345
+ await revokeReplacedAccountToken(
346
+ ctx,
347
+ previous?.accessToken,
348
+ newAccessToken
349
+ );
350
+ }
303
351
  logVerbose(`performLogin: updating big brain auth after login`);
304
- await updateBigBrainAuthAfterLogin(ctx, newAccessToken);
352
+ await updateBigBrainAuthAfterLogin(ctx, newAccessToken, account ?? void 0);
305
353
  logVerbose(`performLogin: checking opt ins, acceptOptIns: ${acceptOptIns}`);
306
354
  const shouldContinue = await optins(ctx, acceptOptIns ?? false);
307
355
  if (!shouldContinue) {
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../../../src/cli/lib/login.ts"],
4
- "sourcesContent": ["import { errors, BaseClient, custom } from \"openid-client\";\nimport {\n bigBrainAPI,\n bigBrainFetch,\n logAndHandleFetchError,\n throwingFetch,\n isWebContainer,\n typedPlatformClient,\n} from \"./utils/utils.js\";\nimport open from \"open\";\nimport { chalkStderr } from \"chalk\";\nimport { provisionHost } from \"./config.js\";\nimport { version } from \"../version.js\";\nimport { Context } from \"../../bundler/context.js\";\nimport {\n changeSpinner,\n logError,\n logFailure,\n logFinishedStep,\n logMessage,\n logOutput,\n logVerbose,\n showSpinner,\n} from \"../../bundler/log.js\";\nimport { Issuer } from \"openid-client\";\nimport { hostname } from \"os\";\nimport { execSync } from \"child_process\";\nimport { promptString, promptYesNo } from \"./utils/prompts.js\";\nimport {\n formatPathForPrinting,\n globalConfigPath,\n modifyGlobalConfig,\n} from \"./utils/globalConfig.js\";\nimport { updateBigBrainAuthAfterLogin } from \"./deploymentSelection.js\";\n\n// Per https://github.com/panva/node-openid-client/tree/main/docs#customizing\ncustom.setHttpOptionsDefaults({\n timeout: parseInt(process.env.OPENID_CLIENT_TIMEOUT || \"10000\"),\n});\n\n/**\n * Whether Big Brain accepts this credential. Takes the header explicitly so\n * callers can check a credential other than the one `ctx` resolved to.\n */\nexport async function isAuthorizedHeader(\n ctx: Context,\n header: string,\n): Promise<boolean> {\n try {\n const resp = await fetch(`${provisionHost}/api/authorize`, {\n method: \"HEAD\",\n headers: {\n Authorization: header,\n \"Convex-Client\": `npm-cli-${version}`,\n },\n });\n // Don't throw an error if this request returns a non-200 status.\n // Big Brain responds with a variety of error codes -- 401 if the token is correctly-formed but not valid, and either 400 or 500 if the token is ill-formed.\n // We only care if this check returns a 200 code (so we can skip logging in again) -- any other errors should be silently skipped and we'll run the whole login flow again.\n return resp.status === 200;\n } catch (e: any) {\n // This `catch` block should only be hit if a network error was encountered\n logError(\n `Unexpected error when authorizing - are you connected to the internet?`,\n );\n return await logAndHandleFetchError(ctx, e);\n }\n}\n\nexport async function checkAuthorization(\n ctx: Context,\n acceptOptIns: boolean,\n): Promise<boolean> {\n const header = ctx.bigBrainAuth()?.header ?? null;\n if (header === null) {\n return false;\n }\n if (!(await isAuthorizedHeader(ctx, header))) {\n return false;\n }\n\n // Check that we have optin as well\n const shouldContinue = await optins(ctx, acceptOptIns);\n if (!shouldContinue) {\n return await ctx.crash({\n exitCode: 1,\n errorType: \"fatal\",\n printedMessage: null,\n });\n }\n return true;\n}\n\nasync function performDeviceAuthorization(\n ctx: Context,\n authClient: BaseClient,\n shouldOpen: boolean,\n vercel?: boolean,\n vercelOverride?: string,\n): Promise<string> {\n // Device authorization flow follows this guide: https://github.com/auth0/auth0-device-flow-cli-sample/blob/9f0f3b76a6cd56ea8d99e76769187ea5102d519d/cli.js\n // License: MIT License\n // Copyright (c) 2019 Auth0 Samples\n /*\n The MIT License (MIT)\n\n Copyright (c) 2019 Auth0 Samples\n\n Permission is hereby granted, free of charge, to any person obtaining a copy\n of this software and associated documentation files (the \"Software\"), to deal\n in the Software without restriction, including without limitation the rights\n to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n copies of the Software, and to permit persons to whom the Software is\n furnished to do so, subject to the following conditions:\n\n The above copyright notice and this permission notice shall be included in all\n copies or substantial portions of the Software.\n\n THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\n SOFTWARE.\n */\n\n // Device Authorization Request - https://tools.ietf.org/html/rfc8628#section-3.1\n // Get authentication URL\n let handle;\n try {\n handle = await authClient.deviceAuthorization();\n } catch {\n // We couldn't get verification URL from the auth provider, proceed with manual auth\n return promptString(ctx, {\n message:\n \"Open https://dashboard.convex.dev/auth, log in and paste the token here:\",\n });\n }\n\n // Device Authorization Response - https://tools.ietf.org/html/rfc8628#section-3.2\n // Open authentication URL\n const { verification_uri_complete, user_code, expires_in } = handle;\n\n // Construct Vercel URL if --vercel flag is used\n const urlToOpen = vercel\n ? `https://vercel.com/sso/integrations/${vercelOverride || \"convex\"}?url=${verification_uri_complete}`\n : verification_uri_complete;\n\n logMessage(\n `Visit ${urlToOpen} to finish logging in.\\n` +\n `You should see the following code which expires in ${\n expires_in % 60 === 0\n ? `${expires_in / 60} minutes`\n : `${expires_in} seconds`\n }: ${user_code}`,\n );\n if (shouldOpen) {\n shouldOpen = await promptYesNo(ctx, {\n message: `Open the browser?`,\n default: true,\n });\n }\n\n if (shouldOpen) {\n showSpinner(`Opening ${urlToOpen} in your browser to log in...\\n`);\n try {\n const p = await open(urlToOpen);\n p.once(\"error\", () => {\n changeSpinner(`Manually open ${urlToOpen} in your browser to log in.`);\n });\n changeSpinner(\"Waiting for the confirmation...\");\n } catch {\n logError(chalkStderr.red(`Unable to open browser.`));\n changeSpinner(`Manually open ${urlToOpen} in your browser to log in.`);\n }\n } else {\n showSpinner(`Open ${urlToOpen} in your browser to log in.`);\n }\n\n // Device Access Token Request - https://tools.ietf.org/html/rfc8628#section-3.4\n // Device Access Token Response - https://tools.ietf.org/html/rfc8628#section-3.5\n try {\n const tokens = await handle.poll();\n if (typeof tokens.access_token === \"string\") {\n return tokens.access_token;\n } else {\n // Unexpected error\n // eslint-disable-next-line no-restricted-syntax\n throw Error(\"Access token is missing\");\n }\n } catch (err: any) {\n switch (err.error) {\n case \"access_denied\": // end-user declined the device confirmation prompt, consent or rules failed\n return await ctx.crash({\n exitCode: 1,\n errorType: \"fatal\",\n printedMessage: \"Access denied.\",\n errForSentry: err,\n });\n case \"expired_token\": // end-user did not complete the interaction in time\n return await ctx.crash({\n exitCode: 1,\n errorType: \"fatal\",\n printedMessage: \"Device flow expired.\",\n errForSentry: err,\n });\n default: {\n const message =\n err instanceof errors.OPError\n ? `Error = ${err.error}; error_description = ${err.error_description}`\n : `Login failed with error: ${err}`;\n return await ctx.crash({\n exitCode: 1,\n errorType: \"fatal\",\n printedMessage: message,\n errForSentry: err,\n });\n }\n }\n }\n}\n\nasync function performPasswordAuthentication(\n ctx: Context,\n clientId: string,\n username: string,\n password: string,\n): Promise<string> {\n if (!process.env.WORKOS_API_SECRET) {\n return await ctx.crash({\n exitCode: 1,\n errorType: \"fatal\",\n printedMessage: \"WORKOS_API_SECRET environment variable is not set\",\n });\n }\n\n // Unfortunately, `openid-client` doesn't support the resource owner password credentials flow so we need to manually send the requests.\n const options: Parameters<typeof throwingFetch>[1] = {\n method: \"POST\",\n headers: { \"Content-Type\": \"application/json\" },\n body: JSON.stringify({\n grant_type: \"password\",\n email: username,\n password: password,\n client_id: clientId,\n client_secret: process.env.WORKOS_API_SECRET,\n }),\n };\n\n try {\n const response = await throwingFetch(\n \"https://apiauth.convex.dev/user_management/authenticate\",\n options,\n );\n const data = await response.json();\n if (typeof data.access_token === \"string\") {\n return data.access_token;\n } else {\n // Unexpected error\n // eslint-disable-next-line no-restricted-syntax\n throw Error(\"Access token is missing\");\n }\n } catch (err: any) {\n logFailure(`Password flow failed: ${err}`);\n if (err.response) {\n logError(chalkStderr.red(`${JSON.stringify(err.response.data)}`));\n }\n return await ctx.crash({\n exitCode: 1,\n errorType: \"fatal\",\n errForSentry: err,\n printedMessage: null,\n });\n }\n}\n\nexport async function performLogin(\n ctx: Context,\n {\n overrideAuthUrl,\n overrideAuthClient,\n overrideAuthUsername,\n overrideAuthPassword,\n overrideAccessToken,\n loginFlow,\n open,\n acceptOptIns,\n dumpAccessToken,\n deviceName: deviceNameOverride,\n anonymousId,\n vercel,\n vercelOverride,\n }: {\n overrideAuthUrl?: string | undefined;\n overrideAuthClient?: string | undefined;\n overrideAuthUsername?: string | undefined;\n overrideAuthPassword?: string | undefined;\n overrideAccessToken?: string | undefined;\n loginFlow?: \"auto\" | \"paste\" | \"poll\" | undefined;\n // default `true`\n open?: boolean | undefined;\n // default `false`\n acceptOptIns?: boolean | undefined;\n dumpAccessToken?: boolean | undefined;\n deviceName?: string | undefined;\n anonymousId?: string | undefined;\n vercel?: boolean | undefined;\n vercelOverride?: string | undefined;\n } = {},\n) {\n loginFlow = loginFlow || \"auto\";\n // Get access token from big-brain\n // Default the device name to the hostname, but allow the user to change this if the terminal is interactive.\n // On Macs, the `hostname()` may be a weirdly-truncated form of the computer name. Attempt to read the \"real\" name before falling back to hostname.\n let deviceName = deviceNameOverride ?? \"\";\n if (!deviceName && process.platform === \"darwin\") {\n try {\n deviceName = execSync(\"scutil --get ComputerName\").toString().trim();\n } catch {\n // Just fall back to the hostname default below.\n }\n }\n if (!deviceName) {\n deviceName = hostname();\n }\n if (!deviceNameOverride) {\n logMessage(\n chalkStderr.bold(\n `Welcome to developing with Convex, let's get you logged in.`,\n ),\n );\n deviceName = await promptString(ctx, {\n message: \"Device name:\",\n default: deviceName,\n });\n }\n\n const issuer = overrideAuthUrl ?? \"https://auth.convex.dev\";\n const clientId = overrideAuthClient ?? \"HFtA247jp9iNs08NTLIB7JsNPMmRIyfi\";\n let accessToken: string;\n\n if (overrideAccessToken) {\n // Access token was supplied directly.\n accessToken = overrideAccessToken;\n } else if (overrideAuthUsername && overrideAuthPassword) {\n // Username/Password auth\n accessToken = await performPasswordAuthentication(\n ctx,\n clientId,\n overrideAuthUsername,\n overrideAuthPassword,\n );\n } else if (\n loginFlow === \"paste\" ||\n (loginFlow === \"auto\" && isWebContainer())\n ) {\n accessToken = await promptString(ctx, {\n message:\n \"Open https://dashboard.convex.dev/auth, log in and paste the token here:\",\n });\n } else {\n // Device authorization flow. Contact OIDC issuer.\n let authIssuer;\n try {\n authIssuer = await Issuer.discover(issuer);\n } catch {\n // Couldn't contact https://auth.convex.dev/.well-known/openid-configuration,\n // proceed with manual auth.\n authIssuer = undefined;\n }\n if (authIssuer) {\n const authClient = new authIssuer.Client({\n client_id: clientId,\n token_endpoint_auth_method: \"none\",\n id_token_signed_response_alg: \"RS256\",\n });\n accessToken = await performDeviceAuthorization(\n ctx,\n authClient,\n open ?? true,\n vercel,\n vercelOverride,\n );\n } else {\n accessToken = await promptString(ctx, {\n message:\n \"Open https://dashboard.convex.dev/auth, log in and paste the token here:\",\n });\n }\n }\n\n if (dumpAccessToken) {\n logOutput(`${accessToken}`);\n return await ctx.crash({\n exitCode: 0,\n errorType: \"fatal\",\n printedMessage: null,\n });\n }\n\n // Exchange the WorkOS access token for a Convex personal access token.\n ctx._updateBigBrainAuth({\n accessToken: accessToken,\n kind: \"accessToken\",\n header: `Bearer ${accessToken}`,\n });\n const response = await typedPlatformClient(ctx).POST(\n \"/create_personal_access_token\",\n {\n // `anonymousId` links a prior anonymous session to this account. It's\n // intentionally absent from the generated platform API types,\n // so cast to pass it through.\n body: { name: deviceName, anonymousId } as { name: string },\n },\n );\n const newAccessToken = response.data!.accessToken;\n const globalConfig = { accessToken: newAccessToken };\n try {\n await modifyGlobalConfig(ctx, globalConfig);\n const path = globalConfigPath();\n logFinishedStep(`Saved credentials to ${formatPathForPrinting(path)}`);\n } catch (err: unknown) {\n return await ctx.crash({\n exitCode: 1,\n errorType: \"invalid filesystem data\",\n errForSentry: err,\n printedMessage: null,\n });\n }\n\n logVerbose(`performLogin: updating big brain auth after login`);\n await updateBigBrainAuthAfterLogin(ctx, newAccessToken);\n\n logVerbose(`performLogin: checking opt ins, acceptOptIns: ${acceptOptIns}`);\n // Do opt in to TOS and Privacy Policy stuff\n const shouldContinue = await optins(ctx, acceptOptIns ?? false);\n if (!shouldContinue) {\n return await ctx.crash({\n exitCode: 1,\n errorType: \"fatal\",\n printedMessage: null,\n });\n }\n\n if (vercel) {\n await promptJoinVercelTeams(ctx);\n }\n}\n\ntype PotentialVercelTeam = {\n teamId: number;\n teamName: string;\n teamSlug: string;\n planId: string;\n planName: string;\n pricingNotice: string | null;\n};\n\n// After `--vercel` login, surface any Vercel-marketplace teams the user has\n// access to but isn't yet a member of, and prompt to join each.\nasync function promptJoinVercelTeams(ctx: Context): Promise<void> {\n const fetch = bigBrainFetch(ctx);\n let teams: PotentialVercelTeam[];\n try {\n const res = await fetch(\n new URL(\"vercel/potential_teams\", `${provisionHost}/`),\n );\n if (!res.ok) {\n logVerbose(\n `vercel/potential_teams returned ${res.status}; skipping team-join prompt`,\n );\n return;\n }\n teams = await res.json();\n } catch (err) {\n logVerbose(`Failed to fetch potential Vercel teams: ${String(err)}`);\n return;\n }\n if (teams.length === 0) return;\n\n for (const [index, team] of teams.entries()) {\n const displayName = team.teamName.replace(/ \\(Vercel\\)$/, \"\");\n const counter = teams.length > 1 ? `[${index + 1}/${teams.length}] ` : \"\";\n const lines = [\n chalkStderr.bold(`${counter}You've been invited to join ${displayName}`) +\n ` (${team.planName}) through the Vercel marketplace.`,\n ];\n if (team.pricingNotice) {\n lines.push(chalkStderr.yellow(team.pricingNotice));\n }\n lines.push(`Join \"${displayName}\"?`);\n const join = await promptYesNo(ctx, {\n message: `${lines.join(\"\\n\")}`,\n default: true,\n });\n if (!join) continue;\n try {\n await fetch(\n new URL(\n `vercel/potential_teams/${team.teamId}/join`,\n `${provisionHost}/`,\n ),\n { method: \"POST\" },\n );\n logFinishedStep(`Joined ${displayName}`);\n } catch (err) {\n logFailure(`Failed to join ${displayName}: ${String(err)}`);\n }\n }\n}\n\n/// There are fields like version, but we keep them opaque\ntype OptIn = Record<string, unknown>;\n\ntype OptInToAccept = {\n optIn: OptIn;\n message: string;\n};\n\ntype AcceptOptInsArgs = {\n optInsAccepted: OptIn[];\n};\n\n// Returns whether we can proceed or not.\nasync function optins(ctx: Context, acceptOptIns: boolean): Promise<boolean> {\n const bbAuth = ctx.bigBrainAuth();\n if (bbAuth === null) {\n // This should never happen, but if we're not even logged in, we can't proceed.\n return false;\n }\n switch (bbAuth.kind) {\n case \"accessToken\":\n break;\n case \"deploymentKey\":\n case \"projectKey\":\n case \"previewDeployKey\":\n // If we have a key configured as auth, we do not need to check opt ins.\n return true;\n default: {\n bbAuth satisfies never;\n return await ctx.crash({\n exitCode: 1,\n errorType: \"fatal\",\n errForSentry: `Unexpected auth kind ${(bbAuth as any).kind}`,\n printedMessage: \"Hit an unexpected error while logging in.\",\n });\n }\n }\n const data = await bigBrainAPI({\n ctx,\n method: \"POST\",\n path: \"check_opt_ins\",\n });\n if (data.optInsToAccept.length === 0) {\n return true;\n }\n for (const optInToAccept of data.optInsToAccept) {\n const confirmed =\n acceptOptIns ||\n (await promptYesNo(ctx, {\n message: optInToAccept.message,\n }));\n if (!confirmed) {\n logFailure(\"Please accept the Terms of Service to use Convex.\");\n return Promise.resolve(false);\n }\n }\n\n const optInsAccepted = data.optInsToAccept.map((o: OptInToAccept) => o.optIn);\n const args: AcceptOptInsArgs = { optInsAccepted };\n await bigBrainAPI({\n ctx,\n method: \"POST\",\n path: \"accept_opt_ins\",\n data: args,\n });\n return true;\n}\n\nexport async function ensureLoggedIn(\n ctx: Context,\n options?: {\n message?: string | undefined;\n overrideAuthUrl?: string | undefined;\n overrideAuthClient?: string | undefined;\n overrideAuthUsername?: string | undefined;\n overrideAuthPassword?: string | undefined;\n },\n) {\n const isLoggedIn = await checkAuthorization(ctx, false);\n if (!isLoggedIn) {\n if (options?.message) {\n logMessage(options.message);\n }\n await performLogin(ctx, {\n acceptOptIns: false,\n overrideAuthUrl: options?.overrideAuthUrl,\n overrideAuthClient: options?.overrideAuthClient,\n overrideAuthUsername: options?.overrideAuthUsername,\n overrideAuthPassword: options?.overrideAuthPassword,\n });\n }\n}\n"],
5
- "mappings": "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",
6
- "names": ["open", "fetch"]
4
+ "sourcesContent": ["import { errors, BaseClient, custom } from \"openid-client\";\nimport {\n bigBrainAPI,\n bigBrainFetch,\n logAndHandleFetchError,\n throwingFetch,\n isWebContainer,\n typedPlatformClient,\n} from \"./utils/utils.js\";\nimport open from \"open\";\nimport { chalkStderr } from \"chalk\";\nimport { provisionHost } from \"./config.js\";\nimport { version } from \"../version.js\";\nimport { Context } from \"../../bundler/context.js\";\nimport {\n changeSpinner,\n logError,\n logFailure,\n logFinishedStep,\n logMessage,\n logOutput,\n logVerbose,\n logWarning,\n showSpinner,\n} from \"../../bundler/log.js\";\nimport { Issuer } from \"openid-client\";\nimport { hostname } from \"os\";\nimport { execSync } from \"child_process\";\nimport { promptString, promptYesNo } from \"./utils/prompts.js\";\nimport {\n formatPathForPrinting,\n globalConfigPath,\n modifyGlobalConfig,\n} from \"./utils/globalConfig.js\";\nimport { updateBigBrainAuthAfterLogin } from \"./deploymentSelection.js\";\nimport {\n getAccountSelection,\n AccountSelection,\n accountConfigPath,\n writeAccountCredentials,\n readAccountCredentials,\n} from \"./accounts.js\";\nimport {\n accountApi,\n accountLoginMetadata,\n revokeReplacedAccountToken,\n} from \"./accountManagement.js\";\n\n// Per https://github.com/panva/node-openid-client/tree/main/docs#customizing\ncustom.setHttpOptionsDefaults({\n timeout: parseInt(process.env.OPENID_CLIENT_TIMEOUT || \"10000\"),\n});\n\n/**\n * Whether Big Brain accepts this credential. Takes the header explicitly so\n * callers can check a credential other than the one `ctx` resolved to.\n */\nexport async function isAuthorizedHeader(\n ctx: Context,\n header: string,\n): Promise<boolean> {\n try {\n const resp = await fetch(`${provisionHost}/api/authorize`, {\n method: \"HEAD\",\n headers: {\n Authorization: header,\n \"Convex-Client\": `npm-cli-${version}`,\n },\n });\n // Don't throw an error if this request returns a non-200 status.\n // Big Brain responds with a variety of error codes -- 401 if the token is correctly-formed but not valid, and either 400 or 500 if the token is ill-formed.\n // We only care if this check returns a 200 code (so we can skip logging in again) -- any other errors should be silently skipped and we'll run the whole login flow again.\n return resp.status === 200;\n } catch (e: any) {\n // This `catch` block should only be hit if a network error was encountered\n logError(\n `Unexpected error when authorizing - are you connected to the internet?`,\n );\n return await logAndHandleFetchError(ctx, e);\n }\n}\n\nexport async function checkAuthorization(\n ctx: Context,\n acceptOptIns: boolean,\n): Promise<boolean> {\n const header = ctx.bigBrainAuth()?.header ?? null;\n if (header === null) {\n return false;\n }\n if (!(await isAuthorizedHeader(ctx, header))) {\n return false;\n }\n\n // Check that we have optin as well\n const shouldContinue = await optins(ctx, acceptOptIns);\n if (!shouldContinue) {\n return await ctx.crash({\n exitCode: 1,\n errorType: \"fatal\",\n printedMessage: null,\n });\n }\n return true;\n}\n\nasync function performDeviceAuthorization(\n ctx: Context,\n authClient: BaseClient,\n shouldOpen: boolean,\n vercel?: boolean,\n vercelOverride?: string,\n): Promise<string> {\n // Device authorization flow follows this guide: https://github.com/auth0/auth0-device-flow-cli-sample/blob/9f0f3b76a6cd56ea8d99e76769187ea5102d519d/cli.js\n // License: MIT License\n // Copyright (c) 2019 Auth0 Samples\n /*\n The MIT License (MIT)\n\n Copyright (c) 2019 Auth0 Samples\n\n Permission is hereby granted, free of charge, to any person obtaining a copy\n of this software and associated documentation files (the \"Software\"), to deal\n in the Software without restriction, including without limitation the rights\n to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n copies of the Software, and to permit persons to whom the Software is\n furnished to do so, subject to the following conditions:\n\n The above copyright notice and this permission notice shall be included in all\n copies or substantial portions of the Software.\n\n THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\n SOFTWARE.\n */\n\n // Device Authorization Request - https://tools.ietf.org/html/rfc8628#section-3.1\n // Get authentication URL\n let handle;\n try {\n handle = await authClient.deviceAuthorization();\n } catch {\n // We couldn't get verification URL from the auth provider, proceed with manual auth\n return promptString(ctx, {\n message:\n \"Open https://dashboard.convex.dev/auth, log in and paste the token here:\",\n });\n }\n\n // Device Authorization Response - https://tools.ietf.org/html/rfc8628#section-3.2\n // Open authentication URL\n const { verification_uri_complete, user_code, expires_in } = handle;\n\n // Construct Vercel URL if --vercel flag is used\n const urlToOpen = vercel\n ? `https://vercel.com/sso/integrations/${vercelOverride || \"convex\"}?url=${verification_uri_complete}`\n : verification_uri_complete;\n\n logMessage(\n `Visit ${urlToOpen} to finish logging in.\\n` +\n `You should see the following code which expires in ${\n expires_in % 60 === 0\n ? `${expires_in / 60} minutes`\n : `${expires_in} seconds`\n }: ${user_code}`,\n );\n if (shouldOpen) {\n shouldOpen = await promptYesNo(ctx, {\n message: `Open the browser?`,\n default: true,\n });\n }\n\n if (shouldOpen) {\n showSpinner(`Opening ${urlToOpen} in your browser to log in...\\n`);\n try {\n const p = await open(urlToOpen);\n p.once(\"error\", () => {\n changeSpinner(`Manually open ${urlToOpen} in your browser to log in.`);\n });\n changeSpinner(\"Waiting for the confirmation...\");\n } catch {\n logError(chalkStderr.red(`Unable to open browser.`));\n changeSpinner(`Manually open ${urlToOpen} in your browser to log in.`);\n }\n } else {\n showSpinner(`Open ${urlToOpen} in your browser to log in.`);\n }\n\n // Device Access Token Request - https://tools.ietf.org/html/rfc8628#section-3.4\n // Device Access Token Response - https://tools.ietf.org/html/rfc8628#section-3.5\n try {\n const tokens = await handle.poll();\n if (typeof tokens.access_token === \"string\") {\n return tokens.access_token;\n } else {\n // Unexpected error\n // eslint-disable-next-line no-restricted-syntax\n throw Error(\"Access token is missing\");\n }\n } catch (err: any) {\n switch (err.error) {\n case \"access_denied\": // end-user declined the device confirmation prompt, consent or rules failed\n return await ctx.crash({\n exitCode: 1,\n errorType: \"fatal\",\n printedMessage: \"Access denied.\",\n errForSentry: err,\n });\n case \"expired_token\": // end-user did not complete the interaction in time\n return await ctx.crash({\n exitCode: 1,\n errorType: \"fatal\",\n printedMessage: \"Device flow expired.\",\n errForSentry: err,\n });\n default: {\n const message =\n err instanceof errors.OPError\n ? `Error = ${err.error}; error_description = ${err.error_description}`\n : `Login failed with error: ${err}`;\n return await ctx.crash({\n exitCode: 1,\n errorType: \"fatal\",\n printedMessage: message,\n errForSentry: err,\n });\n }\n }\n }\n}\n\nasync function performPasswordAuthentication(\n ctx: Context,\n clientId: string,\n username: string,\n password: string,\n): Promise<string> {\n if (!process.env.WORKOS_API_SECRET) {\n return await ctx.crash({\n exitCode: 1,\n errorType: \"fatal\",\n printedMessage: \"WORKOS_API_SECRET environment variable is not set\",\n });\n }\n\n // Unfortunately, `openid-client` doesn't support the resource owner password credentials flow so we need to manually send the requests.\n const options: Parameters<typeof throwingFetch>[1] = {\n method: \"POST\",\n headers: { \"Content-Type\": \"application/json\" },\n body: JSON.stringify({\n grant_type: \"password\",\n email: username,\n password: password,\n client_id: clientId,\n client_secret: process.env.WORKOS_API_SECRET,\n }),\n };\n\n try {\n const response = await throwingFetch(\n \"https://apiauth.convex.dev/user_management/authenticate\",\n options,\n );\n const data = await response.json();\n if (typeof data.access_token === \"string\") {\n return data.access_token;\n } else {\n // Unexpected error\n // eslint-disable-next-line no-restricted-syntax\n throw Error(\"Access token is missing\");\n }\n } catch (err: any) {\n logFailure(`Password flow failed: ${err}`);\n if (err.response) {\n logError(chalkStderr.red(`${JSON.stringify(err.response.data)}`));\n }\n return await ctx.crash({\n exitCode: 1,\n errorType: \"fatal\",\n errForSentry: err,\n printedMessage: null,\n });\n }\n}\n\nexport async function performLogin(\n ctx: Context,\n {\n overrideAuthUrl,\n overrideAuthClient,\n overrideAuthUsername,\n overrideAuthPassword,\n overrideAccessToken,\n loginFlow,\n open,\n acceptOptIns,\n dumpAccessToken,\n deviceName: deviceNameOverride,\n anonymousId,\n vercel,\n vercelOverride,\n account: selectedAccount,\n }: {\n overrideAuthUrl?: string | undefined;\n overrideAuthClient?: string | undefined;\n overrideAuthUsername?: string | undefined;\n overrideAuthPassword?: string | undefined;\n overrideAccessToken?: string | undefined;\n loginFlow?: \"auto\" | \"paste\" | \"poll\" | undefined;\n // default `true`\n open?: boolean | undefined;\n // default `false`\n acceptOptIns?: boolean | undefined;\n dumpAccessToken?: boolean | undefined;\n deviceName?: string | undefined;\n anonymousId?: string | undefined;\n vercel?: boolean | undefined;\n vercelOverride?: string | undefined;\n account?: AccountSelection | null;\n } = {},\n) {\n const auth = ctx.bigBrainAuth();\n const account =\n selectedAccount !== undefined\n ? selectedAccount\n : auth?.kind === \"accessToken\" && auth.account !== undefined\n ? auth.account\n : await getAccountSelection(ctx);\n loginFlow = loginFlow || \"auto\";\n // Get access token from big-brain\n // Default the device name to the hostname, but allow the user to change this if the terminal is interactive.\n // On Macs, the `hostname()` may be a weirdly-truncated form of the computer name. Attempt to read the \"real\" name before falling back to hostname.\n let deviceName = deviceNameOverride ?? \"\";\n if (!deviceName && process.platform === \"darwin\") {\n try {\n deviceName = execSync(\"scutil --get ComputerName\").toString().trim();\n } catch {\n // Just fall back to the hostname default below.\n }\n }\n if (!deviceName) {\n deviceName = hostname();\n }\n if (!deviceNameOverride) {\n logMessage(\n chalkStderr.bold(\n `Welcome to developing with Convex, let's get you logged in.`,\n ),\n );\n deviceName = await promptString(ctx, {\n message: \"Device name:\",\n default: deviceName,\n });\n }\n\n const issuer = overrideAuthUrl ?? \"https://auth.convex.dev\";\n const clientId = overrideAuthClient ?? \"HFtA247jp9iNs08NTLIB7JsNPMmRIyfi\";\n let accessToken: string;\n\n if (overrideAccessToken) {\n // Access token was supplied directly.\n accessToken = overrideAccessToken;\n } else if (overrideAuthUsername && overrideAuthPassword) {\n // Username/Password auth\n accessToken = await performPasswordAuthentication(\n ctx,\n clientId,\n overrideAuthUsername,\n overrideAuthPassword,\n );\n } else if (\n loginFlow === \"paste\" ||\n (loginFlow === \"auto\" && isWebContainer())\n ) {\n accessToken = await promptString(ctx, {\n message:\n \"Open https://dashboard.convex.dev/auth, log in and paste the token here:\",\n });\n } else {\n // Device authorization flow. Contact OIDC issuer.\n let authIssuer;\n try {\n authIssuer = await Issuer.discover(issuer);\n } catch {\n // Couldn't contact https://auth.convex.dev/.well-known/openid-configuration,\n // proceed with manual auth.\n authIssuer = undefined;\n }\n if (authIssuer) {\n const authClient = new authIssuer.Client({\n client_id: clientId,\n token_endpoint_auth_method: \"none\",\n id_token_signed_response_alg: \"RS256\",\n });\n accessToken = await performDeviceAuthorization(\n ctx,\n authClient,\n open ?? true,\n vercel,\n vercelOverride,\n );\n } else {\n accessToken = await promptString(ctx, {\n message:\n \"Open https://dashboard.convex.dev/auth, log in and paste the token here:\",\n });\n }\n }\n\n if (dumpAccessToken) {\n logOutput(`${accessToken}`);\n return await ctx.crash({\n exitCode: 0,\n errorType: \"fatal\",\n printedMessage: null,\n });\n }\n\n // Exchange the WorkOS access token for a Convex personal access token.\n const previous =\n account === null ? null : await readAccountCredentials(ctx, account, true);\n const metadata =\n account === null\n ? {}\n : await accountLoginMetadata(ctx, account, accessToken, previous);\n ctx._updateBigBrainAuth({\n accessToken: accessToken,\n kind: \"accessToken\",\n header: `Bearer ${accessToken}`,\n });\n const response = await typedPlatformClient(ctx).POST(\n \"/create_personal_access_token\",\n {\n // `anonymousId` links a prior anonymous session to this account. It's\n // intentionally absent from the generated platform API types,\n // so cast to pass it through.\n body: { name: deviceName, anonymousId } as { name: string },\n },\n );\n const newAccessToken = response.data!.accessToken;\n const globalConfig = { accessToken: newAccessToken };\n try {\n if (account !== null) {\n await writeAccountCredentials(\n ctx,\n account.name,\n newAccessToken,\n metadata,\n previous,\n async () => {\n try {\n const response = await accountApi(\n newAccessToken,\n \"/v1/delete_personal_access_token\",\n { id: newAccessToken },\n );\n if (response.ok) return;\n } catch {\n // Report cleanup failure alongside the account conflict below.\n }\n logWarning(\n \"The unused login token could not be revoked. You can remove it in the dashboard's account settings.\",\n );\n },\n );\n } else {\n await modifyGlobalConfig(ctx, globalConfig);\n }\n const path =\n account !== null ? accountConfigPath(account.name) : globalConfigPath();\n logFinishedStep(`Saved credentials to ${formatPathForPrinting(path)}`);\n } catch (err: unknown) {\n return await ctx.crash({\n exitCode: 1,\n errorType: \"invalid filesystem data\",\n errForSentry: err,\n printedMessage: null,\n });\n }\n\n if (account !== null) {\n await revokeReplacedAccountToken(\n ctx,\n previous?.accessToken,\n newAccessToken,\n );\n }\n logVerbose(`performLogin: updating big brain auth after login`);\n await updateBigBrainAuthAfterLogin(ctx, newAccessToken, account ?? undefined);\n\n logVerbose(`performLogin: checking opt ins, acceptOptIns: ${acceptOptIns}`);\n // Do opt in to TOS and Privacy Policy stuff\n const shouldContinue = await optins(ctx, acceptOptIns ?? false);\n if (!shouldContinue) {\n return await ctx.crash({\n exitCode: 1,\n errorType: \"fatal\",\n printedMessage: null,\n });\n }\n\n if (vercel) {\n await promptJoinVercelTeams(ctx);\n }\n}\n\ntype PotentialVercelTeam = {\n teamId: number;\n teamName: string;\n teamSlug: string;\n planId: string;\n planName: string;\n pricingNotice: string | null;\n};\n\n// After `--vercel` login, surface any Vercel-marketplace teams the user has\n// access to but isn't yet a member of, and prompt to join each.\nasync function promptJoinVercelTeams(ctx: Context): Promise<void> {\n const fetch = bigBrainFetch(ctx);\n let teams: PotentialVercelTeam[];\n try {\n const res = await fetch(\n new URL(\"vercel/potential_teams\", `${provisionHost}/`),\n );\n if (!res.ok) {\n logVerbose(\n `vercel/potential_teams returned ${res.status}; skipping team-join prompt`,\n );\n return;\n }\n teams = await res.json();\n } catch (err) {\n logVerbose(`Failed to fetch potential Vercel teams: ${String(err)}`);\n return;\n }\n if (teams.length === 0) return;\n\n for (const [index, team] of teams.entries()) {\n const displayName = team.teamName.replace(/ \\(Vercel\\)$/, \"\");\n const counter = teams.length > 1 ? `[${index + 1}/${teams.length}] ` : \"\";\n const lines = [\n chalkStderr.bold(`${counter}You've been invited to join ${displayName}`) +\n ` (${team.planName}) through the Vercel marketplace.`,\n ];\n if (team.pricingNotice) {\n lines.push(chalkStderr.yellow(team.pricingNotice));\n }\n lines.push(`Join \"${displayName}\"?`);\n const join = await promptYesNo(ctx, {\n message: `${lines.join(\"\\n\")}`,\n default: true,\n });\n if (!join) continue;\n try {\n await fetch(\n new URL(\n `vercel/potential_teams/${team.teamId}/join`,\n `${provisionHost}/`,\n ),\n { method: \"POST\" },\n );\n logFinishedStep(`Joined ${displayName}`);\n } catch (err) {\n logFailure(`Failed to join ${displayName}: ${String(err)}`);\n }\n }\n}\n\n/// There are fields like version, but we keep them opaque\ntype OptIn = Record<string, unknown>;\n\ntype OptInToAccept = {\n optIn: OptIn;\n message: string;\n};\n\ntype AcceptOptInsArgs = {\n optInsAccepted: OptIn[];\n};\n\n// Returns whether we can proceed or not.\nasync function optins(ctx: Context, acceptOptIns: boolean): Promise<boolean> {\n const bbAuth = ctx.bigBrainAuth();\n if (bbAuth === null) {\n // This should never happen, but if we're not even logged in, we can't proceed.\n return false;\n }\n switch (bbAuth.kind) {\n case \"accessToken\":\n break;\n case \"deploymentKey\":\n case \"projectKey\":\n case \"previewDeployKey\":\n // If we have a key configured as auth, we do not need to check opt ins.\n return true;\n default: {\n bbAuth satisfies never;\n return await ctx.crash({\n exitCode: 1,\n errorType: \"fatal\",\n errForSentry: `Unexpected auth kind ${(bbAuth as any).kind}`,\n printedMessage: \"Hit an unexpected error while logging in.\",\n });\n }\n }\n const data = await bigBrainAPI({\n ctx,\n method: \"POST\",\n path: \"check_opt_ins\",\n });\n if (data.optInsToAccept.length === 0) {\n return true;\n }\n for (const optInToAccept of data.optInsToAccept) {\n const confirmed =\n acceptOptIns ||\n (await promptYesNo(ctx, {\n message: optInToAccept.message,\n }));\n if (!confirmed) {\n logFailure(\"Please accept the Terms of Service to use Convex.\");\n return Promise.resolve(false);\n }\n }\n\n const optInsAccepted = data.optInsToAccept.map((o: OptInToAccept) => o.optIn);\n const args: AcceptOptInsArgs = { optInsAccepted };\n await bigBrainAPI({\n ctx,\n method: \"POST\",\n path: \"accept_opt_ins\",\n data: args,\n });\n return true;\n}\n\nexport async function ensureLoggedIn(\n ctx: Context,\n options?: {\n message?: string | undefined;\n overrideAuthUrl?: string | undefined;\n overrideAuthClient?: string | undefined;\n overrideAuthUsername?: string | undefined;\n overrideAuthPassword?: string | undefined;\n },\n) {\n const isLoggedIn = await checkAuthorization(ctx, false);\n if (!isLoggedIn) {\n if (options?.message) {\n logMessage(options.message);\n }\n await performLogin(ctx, {\n acceptOptIns: false,\n overrideAuthUrl: options?.overrideAuthUrl,\n overrideAuthClient: options?.overrideAuthClient,\n overrideAuthUsername: options?.overrideAuthUsername,\n overrideAuthPassword: options?.overrideAuthPassword,\n });\n }\n}\n"],
5
+ "mappings": "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",
6
+ "names": ["open", "response", "fetch"]
7
7
  }
@@ -98,7 +98,11 @@ export function encodeDeploymentSelector(projectDir, deployment) {
98
98
  projectDir,
99
99
  deployment
100
100
  };
101
- return `${deployment.kind}:${btoa(JSON.stringify(payload))}`;
101
+ const serialized = JSON.stringify(payload).replace(
102
+ /[\u0080-\uffff]/g,
103
+ (character) => `\\u${character.charCodeAt(0).toString(16).padStart(4, "0")}`
104
+ );
105
+ return `${deployment.kind}:${btoa(serialized)}`;
102
106
  }
103
107
  const payloadSchema = z.object({
104
108
  projectDir: z.string(),
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../../../../src/cli/lib/mcp/requestContext.ts"],
4
- "sourcesContent": ["import { BigBrainAuth, Context, ErrorType } from \"../../../bundler/context.js\";\nimport { Filesystem, nodeFs } from \"../../../bundler/fs.js\";\nimport { Ora } from \"ora\";\nimport {\n DeploymentSelectionWithinProject,\n deploymentSelectionWithinProjectSchema,\n DeploymentSelectionOptions,\n} from \"../api.js\";\nimport { z } from \"zod\";\nimport {\n DeploymentSelection,\n getDeploymentSelection,\n} from \"../deploymentSelection.js\";\n\nexport interface McpOptions extends DeploymentSelectionOptions {\n projectDir?: string;\n disableTools?: string;\n dangerouslyEnableProductionDeployments?: boolean;\n cautiouslyAllowProductionPii?: boolean;\n}\n\nexport class RequestContext implements Context {\n fs: Filesystem;\n deprecationMessagePrinted = false;\n spinner: Ora | undefined;\n _cleanupFns: Record<string, (exitCode: number, err?: any) => Promise<void>> =\n {};\n _bigBrainAuth: BigBrainAuth | null = null;\n constructor(public options: McpOptions) {\n this.fs = nodeFs;\n this.deprecationMessagePrinted = false;\n }\n\n async crash(args: {\n exitCode: number;\n errorType?: ErrorType;\n errForSentry?: any;\n printedMessage: string | null;\n }): Promise<never> {\n const cleanupFns = this._cleanupFns;\n this._cleanupFns = {};\n for (const fn of Object.values(cleanupFns)) {\n await fn(args.exitCode, args.errForSentry);\n }\n // eslint-disable-next-line no-restricted-syntax\n throw new RequestCrash(args.exitCode, args.errorType, args.printedMessage);\n }\n\n flushAndExit() {\n // eslint-disable-next-line no-restricted-syntax\n throw new Error(\"Not implemented\");\n }\n\n registerCleanup(fn: (exitCode: number, err?: any) => Promise<void>): string {\n const handle = crypto.randomUUID();\n this._cleanupFns[handle] = fn;\n return handle;\n }\n\n removeCleanup(handle: string) {\n const value = this._cleanupFns[handle];\n delete this._cleanupFns[handle];\n return value ?? null;\n }\n\n bigBrainAuth(): BigBrainAuth | null {\n return this._bigBrainAuth;\n }\n\n _updateBigBrainAuth(auth: BigBrainAuth | null): void {\n this._bigBrainAuth = auth;\n }\n\n async decodeDeploymentSelector(encoded: string) {\n const { projectDir, deployment } = decodeDeploymentSelector(encoded);\n if (\n deployment.kind === \"prod\" &&\n !this.options.dangerouslyEnableProductionDeployments\n ) {\n return await this.crash({\n exitCode: 1,\n errorType: \"fatal\",\n printedMessage:\n \"This tool cannot be used with production deployments. Use a read-only tool like `insights` instead, or enable production access with --dangerously-enable-production-deployments.\",\n });\n }\n return { projectDir, deployment };\n }\n\n /** Decode a deployment selector without checking the production guard. Use for read-only tools that don't expose PII (e.g. insights). */\n decodeDeploymentSelectorUnchecked(encoded: string) {\n return decodeDeploymentSelector(encoded);\n }\n\n /** Decode a deployment selector for read-only tools that may expose PII (e.g. data, logs, queries). Requires --cautiously-allow-production-pii. */\n async decodeDeploymentSelectorReadOnly(encoded: string) {\n const { projectDir, deployment } = decodeDeploymentSelector(encoded);\n if (\n deployment.kind === \"prod\" &&\n !this.options.dangerouslyEnableProductionDeployments &&\n !this.options.cautiouslyAllowProductionPii\n ) {\n return await this.crash({\n exitCode: 1,\n errorType: \"fatal\",\n printedMessage:\n \"This read-only tool may expose PII from production. Enable with --cautiously-allow-production-pii, or use --dangerously-enable-production-deployments for full access.\",\n });\n }\n return { projectDir, deployment };\n }\n\n get productionDeploymentsDisabled() {\n return !this.options.dangerouslyEnableProductionDeployments;\n }\n\n get productionPiiAllowed() {\n return (\n this.options.dangerouslyEnableProductionDeployments ||\n this.options.cautiouslyAllowProductionPii\n );\n }\n}\n\nexport class RequestCrash {\n printedMessage: string;\n constructor(\n private exitCode: number,\n private errorType: ErrorType | undefined,\n printedMessage: string | null,\n ) {\n this.printedMessage = printedMessage ?? \"Unknown error\";\n }\n\n toString(): string {\n return this.printedMessage;\n }\n}\n\n// Unfortunately, MCP clients don't seem to handle nested JSON objects very\n// well (even though this is within spec). To work around this, encode the\n// deployment selectors as an obfuscated string that the MCP client can\n// opaquely pass around.\nexport function encodeDeploymentSelector(\n projectDir: string,\n deployment: DeploymentSelectionWithinProject,\n) {\n const payload = {\n projectDir,\n deployment,\n };\n return `${deployment.kind}:${btoa(JSON.stringify(payload))}`;\n}\n\nconst payloadSchema = z.object({\n projectDir: z.string(),\n deployment: deploymentSelectionWithinProjectSchema,\n});\n\nfunction decodeDeploymentSelector(encoded: string) {\n const [_, serializedPayload] = encoded.split(\":\");\n return payloadSchema.parse(JSON.parse(atob(serializedPayload)));\n}\n\n/**\n * Get the deployment selection for MCP tools. The agent can pass different\n * values of `selectionWithinProject` into a tool, so we overwrite the\n * `selectionWithinProject` of the `DeploymentSelection` if it exists.\n */\nexport async function getMcpDeploymentSelection(\n ctx: RequestContext,\n decodedDeploymentSelector: DeploymentSelectionWithinProject,\n): Promise<DeploymentSelection> {\n const initialSelection = await getDeploymentSelection(ctx, ctx.options);\n const hasSelectionWithinProject =\n initialSelection.kind !== \"existingDeployment\";\n return {\n ...initialSelection,\n ...(hasSelectionWithinProject && {\n selectionWithinProject: decodedDeploymentSelector,\n }),\n };\n}\n"],
5
- "mappings": "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",
4
+ "sourcesContent": ["import { BigBrainAuth, Context, ErrorType } from \"../../../bundler/context.js\";\nimport { Filesystem, nodeFs } from \"../../../bundler/fs.js\";\nimport { Ora } from \"ora\";\nimport {\n DeploymentSelectionWithinProject,\n deploymentSelectionWithinProjectSchema,\n DeploymentSelectionOptions,\n} from \"../api.js\";\nimport { z } from \"zod\";\nimport {\n DeploymentSelection,\n getDeploymentSelection,\n} from \"../deploymentSelection.js\";\n\nexport interface McpOptions extends DeploymentSelectionOptions {\n projectDir?: string;\n disableTools?: string;\n dangerouslyEnableProductionDeployments?: boolean;\n cautiouslyAllowProductionPii?: boolean;\n}\n\nexport class RequestContext implements Context {\n fs: Filesystem;\n deprecationMessagePrinted = false;\n spinner: Ora | undefined;\n _cleanupFns: Record<string, (exitCode: number, err?: any) => Promise<void>> =\n {};\n _bigBrainAuth: BigBrainAuth | null = null;\n constructor(public options: McpOptions) {\n this.fs = nodeFs;\n this.deprecationMessagePrinted = false;\n }\n\n async crash(args: {\n exitCode: number;\n errorType?: ErrorType;\n errForSentry?: any;\n printedMessage: string | null;\n }): Promise<never> {\n const cleanupFns = this._cleanupFns;\n this._cleanupFns = {};\n for (const fn of Object.values(cleanupFns)) {\n await fn(args.exitCode, args.errForSentry);\n }\n // eslint-disable-next-line no-restricted-syntax\n throw new RequestCrash(args.exitCode, args.errorType, args.printedMessage);\n }\n\n flushAndExit() {\n // eslint-disable-next-line no-restricted-syntax\n throw new Error(\"Not implemented\");\n }\n\n registerCleanup(fn: (exitCode: number, err?: any) => Promise<void>): string {\n const handle = crypto.randomUUID();\n this._cleanupFns[handle] = fn;\n return handle;\n }\n\n removeCleanup(handle: string) {\n const value = this._cleanupFns[handle];\n delete this._cleanupFns[handle];\n return value ?? null;\n }\n\n bigBrainAuth(): BigBrainAuth | null {\n return this._bigBrainAuth;\n }\n\n _updateBigBrainAuth(auth: BigBrainAuth | null): void {\n this._bigBrainAuth = auth;\n }\n\n async decodeDeploymentSelector(encoded: string) {\n const { projectDir, deployment } = decodeDeploymentSelector(encoded);\n if (\n deployment.kind === \"prod\" &&\n !this.options.dangerouslyEnableProductionDeployments\n ) {\n return await this.crash({\n exitCode: 1,\n errorType: \"fatal\",\n printedMessage:\n \"This tool cannot be used with production deployments. Use a read-only tool like `insights` instead, or enable production access with --dangerously-enable-production-deployments.\",\n });\n }\n return { projectDir, deployment };\n }\n\n /** Decode a deployment selector without checking the production guard. Use for read-only tools that don't expose PII (e.g. insights). */\n decodeDeploymentSelectorUnchecked(encoded: string) {\n return decodeDeploymentSelector(encoded);\n }\n\n /** Decode a deployment selector for read-only tools that may expose PII (e.g. data, logs, queries). Requires --cautiously-allow-production-pii. */\n async decodeDeploymentSelectorReadOnly(encoded: string) {\n const { projectDir, deployment } = decodeDeploymentSelector(encoded);\n if (\n deployment.kind === \"prod\" &&\n !this.options.dangerouslyEnableProductionDeployments &&\n !this.options.cautiouslyAllowProductionPii\n ) {\n return await this.crash({\n exitCode: 1,\n errorType: \"fatal\",\n printedMessage:\n \"This read-only tool may expose PII from production. Enable with --cautiously-allow-production-pii, or use --dangerously-enable-production-deployments for full access.\",\n });\n }\n return { projectDir, deployment };\n }\n\n get productionDeploymentsDisabled() {\n return !this.options.dangerouslyEnableProductionDeployments;\n }\n\n get productionPiiAllowed() {\n return (\n this.options.dangerouslyEnableProductionDeployments ||\n this.options.cautiouslyAllowProductionPii\n );\n }\n}\n\nexport class RequestCrash {\n printedMessage: string;\n constructor(\n private exitCode: number,\n private errorType: ErrorType | undefined,\n printedMessage: string | null,\n ) {\n this.printedMessage = printedMessage ?? \"Unknown error\";\n }\n\n toString(): string {\n return this.printedMessage;\n }\n}\n\n// Unfortunately, MCP clients don't seem to handle nested JSON objects very\n// well (even though this is within spec). To work around this, encode the\n// deployment selectors as an obfuscated string that the MCP client can\n// opaquely pass around.\nexport function encodeDeploymentSelector(\n projectDir: string,\n deployment: DeploymentSelectionWithinProject,\n) {\n const payload = {\n projectDir,\n deployment,\n };\n // ASCII JSON escapes support Unicode paths with the existing Latin-1 decoder.\n const serialized = JSON.stringify(payload).replace(\n /[\\u0080-\\uffff]/g,\n (character) =>\n `\\\\u${character.charCodeAt(0).toString(16).padStart(4, \"0\")}`,\n );\n return `${deployment.kind}:${btoa(serialized)}`;\n}\n\nconst payloadSchema = z.object({\n projectDir: z.string(),\n deployment: deploymentSelectionWithinProjectSchema,\n});\n\nfunction decodeDeploymentSelector(encoded: string) {\n const [_, serializedPayload] = encoded.split(\":\");\n return payloadSchema.parse(JSON.parse(atob(serializedPayload)));\n}\n\n/**\n * Get the deployment selection for MCP tools. The agent can pass different\n * values of `selectionWithinProject` into a tool, so we overwrite the\n * `selectionWithinProject` of the `DeploymentSelection` if it exists.\n */\nexport async function getMcpDeploymentSelection(\n ctx: RequestContext,\n decodedDeploymentSelector: DeploymentSelectionWithinProject,\n): Promise<DeploymentSelection> {\n const initialSelection = await getDeploymentSelection(ctx, ctx.options);\n const hasSelectionWithinProject =\n initialSelection.kind !== \"existingDeployment\";\n return {\n ...initialSelection,\n ...(hasSelectionWithinProject && {\n selectionWithinProject: decodedDeploymentSelector,\n }),\n };\n}\n"],
5
+ "mappings": "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",
6
6
  "names": []
7
7
  }
@@ -0,0 +1,160 @@
1
+ "use strict";
2
+ import os from "os";
3
+ import path from "path";
4
+ import * as Sentry from "@sentry/node";
5
+ import { z } from "zod";
6
+ import {
7
+ convexMarketplaces
8
+ } from "./marketplaces.js";
9
+ import {
10
+ fetchGitHubJson,
11
+ onPath,
12
+ parseList,
13
+ subfolders,
14
+ versionOf
15
+ } from "./utils.js";
16
+ export const createGetClaudeCodeLocations = (places) => async () => {
17
+ const found = [
18
+ ...onPath("claude", places.path).map((p) => ({
19
+ source: "terminal",
20
+ path: p
21
+ })),
22
+ // The desktop app keeps every version it downloaded, as
23
+ // `<version>/<hash>/claude.app`, next to its own state files.
24
+ ...subfolders(
25
+ path.join(
26
+ places.home,
27
+ "Library",
28
+ "Application Support",
29
+ "Claude",
30
+ "claude-code"
31
+ )
32
+ ).flatMap(subfolders).map((dir) => ({
33
+ source: "desktop",
34
+ path: path.join(dir, "claude.app", "Contents", "MacOS", "claude")
35
+ })),
36
+ ...subfolders(path.join(places.home, ".vscode", "extensions")).filter(
37
+ (dir) => path.basename(dir).startsWith("anthropic.claude-code-")
38
+ ).map((dir) => ({
39
+ source: "vscode",
40
+ path: path.join(dir, "resources", "native-binary", "claude")
41
+ }))
42
+ ];
43
+ return Promise.all(
44
+ found.map(async (cli) => ({
45
+ ...cli,
46
+ version: await versionOf(cli.path)
47
+ }))
48
+ );
49
+ };
50
+ export const getClaudeCodeLocations = createGetClaudeCodeLocations({
51
+ home: os.homedir(),
52
+ path: process.env.PATH ?? ""
53
+ });
54
+ export async function claudeCodeLatest() {
55
+ const marketplace = await fetchGitHubJson("anthropics/claude-plugins-official/main/.claude-plugin/marketplace.json");
56
+ if (marketplace === null) return {};
57
+ const source = marketplace.plugins.find((p) => p.name === "convex")?.source;
58
+ if (source === void 0) {
59
+ Sentry.captureMessage(
60
+ "Anthropic's marketplace has no convex plugin",
61
+ "error"
62
+ );
63
+ return {};
64
+ }
65
+ const repo = source.url.replace(/^https:\/\/github\.com\//, "").replace(/\.git$/, "");
66
+ const plugin = await fetchGitHubJson(
67
+ `${repo}/${source.sha}/.claude-plugin/plugin.json`
68
+ );
69
+ return plugin === null ? {} : { convex: plugin.version };
70
+ }
71
+ const claudeCodeMarketplaceList = z.array(
72
+ z.union([
73
+ z.object({
74
+ name: z.string(),
75
+ source: z.literal("github"),
76
+ repo: z.string()
77
+ }),
78
+ z.object({ name: z.string(), source: z.literal("git"), url: z.string() }),
79
+ z.object({
80
+ name: z.string(),
81
+ source: z.literal("directory"),
82
+ path: z.string()
83
+ }),
84
+ // The list holds other people's marketplaces too, so a source type we
85
+ // haven't seen is kept by its type name instead of rejecting the list.
86
+ z.object({ name: z.string(), source: z.string() })
87
+ ])
88
+ );
89
+ function claudeCodeSource(m) {
90
+ if ("repo" in m) return { kind: "github", repo: m.repo };
91
+ if ("url" in m) return { kind: "git", url: m.url };
92
+ if ("path" in m) return { kind: "folder", path: m.path };
93
+ return { kind: "other", type: m.source };
94
+ }
95
+ const claudeCodePluginList = z.array(
96
+ z.object({
97
+ id: z.string(),
98
+ version: z.string(),
99
+ scope: z.string(),
100
+ enabled: z.boolean(),
101
+ folderVersion: z.string().optional()
102
+ })
103
+ );
104
+ function claudeCodePlugins(pluginList) {
105
+ const plugins = parseList(
106
+ claudeCodePluginList,
107
+ pluginList,
108
+ "claude plugin list --json"
109
+ );
110
+ if (plugins === null) return null;
111
+ return plugins.map((p) => {
112
+ const [name, marketplace] = p.id.split("@");
113
+ return {
114
+ name,
115
+ marketplace,
116
+ version: p.folderVersion ?? p.version,
117
+ scope: p.scope,
118
+ enabled: p.enabled,
119
+ cachedVersion: p.folderVersion !== void 0 && p.folderVersion !== p.version ? p.version : void 0
120
+ };
121
+ });
122
+ }
123
+ export function claudeCodeMarketplaces(marketplaceList, pluginList) {
124
+ const marketplaces = parseList(
125
+ claudeCodeMarketplaceList,
126
+ marketplaceList,
127
+ "claude plugin marketplace list --json"
128
+ );
129
+ const plugins = claudeCodePlugins(pluginList);
130
+ if (marketplaces === null || plugins === null) return null;
131
+ return convexMarketplaces(
132
+ marketplaces.map((m) => ({ name: m.name, source: claudeCodeSource(m) })),
133
+ plugins
134
+ );
135
+ }
136
+ const pluginId = "convex@claude-plugins-official";
137
+ const upgradeStep = ["plugin", "update", pluginId, "--scope", "user"];
138
+ export const claudeCode = {
139
+ id: "claudeCode",
140
+ name: "Claude Code",
141
+ plugin: pluginId,
142
+ upgradeStep,
143
+ installSteps: [
144
+ // Notes:
145
+ //
146
+ // 1. Claude Code updates its local copy of the marketplace when it
147
+ // installs, but `plugin install` skips a plugin that's already
148
+ // installed, so an old version stays until the plugin is updated.
149
+ // Add the marketplace
150
+ ["plugin", "marketplace", "add", "anthropics/claude-plugins-official"],
151
+ // Install the plugin if it isn't installed yet
152
+ ["plugin", "install", pluginId, "--scope", "user"],
153
+ // Update an installed plugin to the latest version
154
+ upgradeStep
155
+ ],
156
+ plugins: claudeCodePlugins,
157
+ marketplaces: claudeCodeMarketplaces,
158
+ latest: claudeCodeLatest
159
+ };
160
+ //# sourceMappingURL=claudeCode.js.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../../../../../src/cli/lib/plugins/claudeCode.ts"],
4
+ "sourcesContent": ["import os from \"os\";\nimport path from \"path\";\nimport * as Sentry from \"@sentry/node\";\nimport { z } from \"zod\";\nimport type { Cli, Places } from \"./harness.js\";\nimport {\n convexMarketplaces,\n type Marketplace,\n type Plugin,\n type Source,\n} from \"./marketplaces.js\";\nimport {\n fetchGitHubJson,\n onPath,\n parseList,\n subfolders,\n versionOf,\n} from \"./utils.js\";\n\n/** Builds `getClaudeCodeLocations` for the given places, so tests can point it at fixtures. */\nexport const createGetClaudeCodeLocations =\n (places: Omit<Places, \"applications\">) => async (): Promise<Cli[]> => {\n const found: Omit<Cli, \"version\">[] = [\n ...onPath(\"claude\", places.path).map((p) => ({\n source: \"terminal\" as const,\n path: p,\n })),\n // The desktop app keeps every version it downloaded, as\n // `<version>/<hash>/claude.app`, next to its own state files.\n ...subfolders(\n path.join(\n places.home,\n \"Library\",\n \"Application Support\",\n \"Claude\",\n \"claude-code\",\n ),\n )\n .flatMap(subfolders)\n .map((dir) => ({\n source: \"desktop\" as const,\n path: path.join(dir, \"claude.app\", \"Contents\", \"MacOS\", \"claude\"),\n })),\n ...subfolders(path.join(places.home, \".vscode\", \"extensions\"))\n .filter((dir) =>\n path.basename(dir).startsWith(\"anthropic.claude-code-\"),\n )\n .map((dir) => ({\n source: \"vscode\" as const,\n path: path.join(dir, \"resources\", \"native-binary\", \"claude\"),\n })),\n ];\n return Promise.all(\n found.map(async (cli) => ({\n ...cli,\n version: await versionOf(cli.path),\n })),\n );\n };\n\n/** Every copy of Claude Code on this machine: the terminal CLI, the Claude desktop app's copies and the VS Code extension's copies. */\nexport const getClaudeCodeLocations = createGetClaudeCodeLocations({\n home: os.homedir(),\n path: process.env.PATH ?? \"\",\n});\n\n/** The newest Convex plugin a Claude Code user can install, which is the commit Anthropic's marketplace pins, not our main. */\nexport async function claudeCodeLatest(): Promise<Record<string, string>> {\n const marketplace = await fetchGitHubJson<{\n plugins: { name: string; source: { url: string; sha: string } }[];\n }>(\"anthropics/claude-plugins-official/main/.claude-plugin/marketplace.json\");\n if (marketplace === null) return {};\n const source = marketplace.plugins.find((p) => p.name === \"convex\")?.source;\n if (source === undefined) {\n Sentry.captureMessage(\n \"Anthropic's marketplace has no convex plugin\",\n \"error\",\n );\n return {};\n }\n const repo = source.url\n .replace(/^https:\\/\\/github\\.com\\//, \"\")\n .replace(/\\.git$/, \"\");\n const plugin = await fetchGitHubJson<{ version: string }>(\n `${repo}/${source.sha}/.claude-plugin/plugin.json`,\n );\n return plugin === null ? {} : { convex: plugin.version };\n}\n\n/** `claude plugin marketplace list --json` */\nconst claudeCodeMarketplaceList = z.array(\n z.union([\n z.object({\n name: z.string(),\n source: z.literal(\"github\"),\n repo: z.string(),\n }),\n z.object({ name: z.string(), source: z.literal(\"git\"), url: z.string() }),\n z.object({\n name: z.string(),\n source: z.literal(\"directory\"),\n path: z.string(),\n }),\n // The list holds other people's marketplaces too, so a source type we\n // haven't seen is kept by its type name instead of rejecting the list.\n z.object({ name: z.string(), source: z.string() }),\n ]),\n);\n\n/** Where a Claude Code marketplace comes from. zod keeps only the fields of the shape that matched. */\nfunction claudeCodeSource(\n m: z.infer<typeof claudeCodeMarketplaceList>[number],\n): Source {\n if (\"repo\" in m) return { kind: \"github\", repo: m.repo };\n if (\"url\" in m) return { kind: \"git\", url: m.url };\n if (\"path\" in m) return { kind: \"folder\", path: m.path };\n return { kind: \"other\", type: m.source };\n}\n\n/** `claude plugin list --json` */\nconst claudeCodePluginList = z.array(\n z.object({\n id: z.string(),\n version: z.string(),\n scope: z.string(),\n enabled: z.boolean(),\n folderVersion: z.string().optional(),\n }),\n);\n\n/** Reads the plugins in `claude plugin list --json`, or returns null when it doesn't match. */\nfunction claudeCodePlugins(pluginList: string): Plugin[] | null {\n const plugins = parseList(\n claudeCodePluginList,\n pluginList,\n \"claude plugin list --json\",\n );\n if (plugins === null) return null;\n return plugins.map((p) => {\n const [name, marketplace] = p.id.split(\"@\");\n // A folder install runs from the folder, and `version` is the copy cached at install time.\n return {\n name,\n marketplace,\n version: p.folderVersion ?? p.version,\n scope: p.scope,\n enabled: p.enabled,\n cachedVersion:\n p.folderVersion !== undefined && p.folderVersion !== p.version\n ? p.version\n : undefined,\n };\n });\n}\n\n/** 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. */\nexport function claudeCodeMarketplaces(\n marketplaceList: string,\n pluginList: string,\n): Marketplace[] | null {\n const marketplaces = parseList(\n claudeCodeMarketplaceList,\n marketplaceList,\n \"claude plugin marketplace list --json\",\n );\n const plugins = claudeCodePlugins(pluginList);\n if (marketplaces === null || plugins === null) return null;\n return convexMarketplaces(\n marketplaces.map((m) => ({ name: m.name, source: claudeCodeSource(m) })),\n plugins,\n );\n}\n\nconst pluginId = \"convex@claude-plugins-official\";\n/** The install step that brings an installed plugin to the latest version. When it fails, the user keeps an old version. */\nconst upgradeStep = [\"plugin\", \"update\", pluginId, \"--scope\", \"user\"];\n\n/** Claude Code as `npx convex plugins` installs and reads it. */\nexport const claudeCode = {\n id: \"claudeCode\" as const,\n name: \"Claude Code\",\n plugin: pluginId,\n upgradeStep,\n installSteps: [\n // Notes:\n //\n // 1. Claude Code updates its local copy of the marketplace when it\n // installs, but `plugin install` skips a plugin that's already\n // installed, so an old version stays until the plugin is updated.\n\n // Add the marketplace\n [\"plugin\", \"marketplace\", \"add\", \"anthropics/claude-plugins-official\"],\n // Install the plugin if it isn't installed yet\n [\"plugin\", \"install\", pluginId, \"--scope\", \"user\"],\n // Update an installed plugin to the latest version\n upgradeStep,\n ],\n plugins: claudeCodePlugins,\n marketplaces: claudeCodeMarketplaces,\n latest: claudeCodeLatest,\n};\n"],
5
+ "mappings": "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",
6
+ "names": []
7
+ }
@@ -0,0 +1,140 @@
1
+ "use strict";
2
+ import os from "os";
3
+ import path from "path";
4
+ import { z } from "zod";
5
+ import { nodeFs } from "../../../bundler/fs.js";
6
+ import {
7
+ convexMarketplaces
8
+ } from "./marketplaces.js";
9
+ import {
10
+ fetchGitHubJson,
11
+ onPath,
12
+ parseList,
13
+ subfolders,
14
+ versionOf
15
+ } from "./utils.js";
16
+ export const createGetCodexLocations = (places) => async () => {
17
+ const desktop = path.join(
18
+ places.applications,
19
+ "ChatGPT.app",
20
+ "Contents",
21
+ "Resources",
22
+ "codex-cli",
23
+ "CodexCLI.app",
24
+ "Contents",
25
+ "MacOS",
26
+ "codex"
27
+ );
28
+ const found = [
29
+ ...onPath("codex", places.path).map((p) => ({
30
+ source: "terminal",
31
+ path: p
32
+ })),
33
+ ...nodeFs.exists(desktop) ? [{ source: "desktop", path: desktop }] : [],
34
+ // The extension keeps its binary in a folder named for the platform, like
35
+ // `bin/macos-aarch64`.
36
+ ...subfolders(path.join(places.home, ".vscode", "extensions")).filter((dir) => path.basename(dir).startsWith("openai.chatgpt-")).flatMap((dir) => subfolders(path.join(dir, "bin"))).map((dir) => ({
37
+ source: "vscode",
38
+ path: path.join(dir, "codex")
39
+ }))
40
+ ];
41
+ return Promise.all(
42
+ found.map(async (cli) => ({
43
+ ...cli,
44
+ version: await versionOf(cli.path)
45
+ }))
46
+ );
47
+ };
48
+ export const getCodexLocations = createGetCodexLocations({
49
+ home: os.homedir(),
50
+ path: process.env.PATH ?? "",
51
+ applications: "/Applications"
52
+ });
53
+ async function codexLatest() {
54
+ const plugin = await fetchGitHubJson(
55
+ "convex/convex-codex-plugin/main/plugins/convex/.codex-plugin/plugin.json"
56
+ );
57
+ return plugin === null ? {} : { convex: plugin.version };
58
+ }
59
+ const codexMarketplaceList = z.object({
60
+ marketplaces: z.array(
61
+ z.object({
62
+ name: z.string(),
63
+ root: z.string(),
64
+ marketplaceSource: z.object({ sourceType: z.string(), source: z.string() }).optional()
65
+ })
66
+ )
67
+ });
68
+ const codexPluginList = z.object({
69
+ installed: z.array(
70
+ z.object({
71
+ name: z.string(),
72
+ marketplaceName: z.string(),
73
+ version: z.string(),
74
+ enabled: z.boolean()
75
+ })
76
+ )
77
+ });
78
+ function codexPlugins(pluginList) {
79
+ const plugins = parseList(
80
+ codexPluginList,
81
+ pluginList,
82
+ "codex plugin list --json"
83
+ );
84
+ if (plugins === null) return null;
85
+ return plugins.installed.map((p) => ({
86
+ name: p.name,
87
+ marketplace: p.marketplaceName,
88
+ version: p.version,
89
+ enabled: p.enabled
90
+ }));
91
+ }
92
+ export function codexMarketplaces(marketplaceList, pluginList) {
93
+ const marketplaces = parseList(
94
+ codexMarketplaceList,
95
+ marketplaceList,
96
+ "codex plugin marketplace list --json"
97
+ );
98
+ const plugins = codexPlugins(pluginList);
99
+ if (marketplaces === null || plugins === null) return null;
100
+ return convexMarketplaces(
101
+ marketplaces.marketplaces.map((m) => ({
102
+ name: m.name,
103
+ // Anything that isn't a git marketplace is shown by its folder.
104
+ source: m.marketplaceSource?.sourceType === "git" ? { kind: "git", url: m.marketplaceSource.source } : { kind: "folder", path: m.root }
105
+ })),
106
+ plugins
107
+ );
108
+ }
109
+ const pluginId = "convex@convex-codex-plugin";
110
+ const upgradeStep = ["plugin", "marketplace", "upgrade", "convex-codex-plugin"];
111
+ export const codex = {
112
+ id: "codex",
113
+ name: "Codex",
114
+ plugin: pluginId,
115
+ upgradeStep,
116
+ installSteps: [
117
+ // Notes:
118
+ //
119
+ // 1. The marketplace is added from the `get-convex` org, the org's name
120
+ // before the rename to `convex`. Codex refuses to re-add a marketplace
121
+ // from a different source, so switching would make install fail for
122
+ // anyone who installed before. Switch once install handles an existing
123
+ // marketplace.
124
+ //
125
+ // 2. Codex installs plugins from its own local copy of each marketplace, so
126
+ // it doesn't see the new plugin versions we publish until that copy is
127
+ // updated.
128
+ // Add the marketplace
129
+ ["plugin", "marketplace", "add", "get-convex/convex-codex-plugin"],
130
+ // Update the local copy from GitHub, so the latest plugin version is
131
+ // available to install - this is important
132
+ upgradeStep,
133
+ // Install the latest plugin from the updated marketplace
134
+ ["plugin", "add", pluginId]
135
+ ],
136
+ plugins: codexPlugins,
137
+ marketplaces: codexMarketplaces,
138
+ latest: codexLatest
139
+ };
140
+ //# sourceMappingURL=codex.js.map