create-whop-kit 1.0.7 → 1.0.8

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.
@@ -7,8 +7,8 @@ import {
7
7
  } from "./chunk-42L7PRMT.js";
8
8
 
9
9
  // src/deploy/index.ts
10
- import * as p3 from "@clack/prompts";
11
- import pc3 from "picocolors";
10
+ import * as p4 from "@clack/prompts";
11
+ import pc4 from "picocolors";
12
12
 
13
13
  // src/deploy/vercel.ts
14
14
  import * as p from "@clack/prompts";
@@ -194,6 +194,8 @@ function getGitHubOrgs() {
194
194
  }
195
195
 
196
196
  // src/deploy/whop-api.ts
197
+ import * as p3 from "@clack/prompts";
198
+ import pc3 from "picocolors";
197
199
  var WHOP_API = "https://api.whop.com/api/v1";
198
200
  function headers(apiKey) {
199
201
  return {
@@ -211,13 +213,40 @@ async function validateApiKey(apiKey) {
211
213
  return false;
212
214
  }
213
215
  }
214
- async function createWhopApp(apiKey, name, redirectUris) {
216
+ async function getCompanyId(apiKey) {
217
+ try {
218
+ const res = await fetch(`${WHOP_API}/companies`, {
219
+ headers: headers(apiKey)
220
+ });
221
+ if (res.ok) {
222
+ const data = await res.json();
223
+ const companies = data.data || data;
224
+ if (Array.isArray(companies) && companies.length > 0) {
225
+ return companies[0].id;
226
+ }
227
+ }
228
+ } catch {
229
+ }
230
+ p3.log.info(pc3.dim("Could not detect your company ID automatically."));
231
+ const result = await p3.text({
232
+ message: "Your Whop Company ID",
233
+ placeholder: "biz_xxxxxxxxx (find it in your dashboard URL)",
234
+ validate: (v) => {
235
+ if (!v) return "Required";
236
+ if (!v.startsWith("biz_")) return 'Must start with "biz_"';
237
+ }
238
+ });
239
+ if (p3.isCancel(result)) return null;
240
+ return result;
241
+ }
242
+ async function createWhopApp(apiKey, name, redirectUris, companyId) {
215
243
  try {
216
244
  const res = await fetch(`${WHOP_API}/apps`, {
217
245
  method: "POST",
218
246
  headers: headers(apiKey),
219
247
  body: JSON.stringify({
220
248
  name,
249
+ company_id: companyId,
221
250
  redirect_uris: redirectUris
222
251
  })
223
252
  });
@@ -233,12 +262,12 @@ async function createWhopApp(apiKey, name, redirectUris) {
233
262
  return null;
234
263
  }
235
264
  }
236
- async function createWhopWebhook(apiKey, url, events) {
265
+ async function createWhopWebhook(apiKey, url, events, companyId) {
237
266
  try {
238
267
  const res = await fetch(`${WHOP_API}/webhooks`, {
239
268
  method: "POST",
240
269
  headers: headers(apiKey),
241
- body: JSON.stringify({ url, events })
270
+ body: JSON.stringify({ url, events, company_id: companyId })
242
271
  });
243
272
  if (res.ok) {
244
273
  const data = await res.json();
@@ -273,7 +302,7 @@ function openUrl(url) {
273
302
  }
274
303
  async function runDeployPipeline(options) {
275
304
  const { projectDir, projectName, databaseUrl, framework } = options;
276
- const setupMode = await p3.select({
305
+ const setupMode = await p4.select({
277
306
  message: "How would you like to deploy?",
278
307
  options: [
279
308
  {
@@ -293,32 +322,32 @@ async function runDeployPipeline(options) {
293
322
  }
294
323
  ]
295
324
  });
296
- if (p3.isCancel(setupMode)) return null;
325
+ if (p4.isCancel(setupMode)) return null;
297
326
  const useGithub = setupMode === "github-vercel" || setupMode === "github-only";
298
327
  const useVercel = setupMode === "github-vercel" || setupMode === "vercel-only";
299
328
  let githubRepoUrl = null;
300
329
  let productionUrl = null;
301
330
  if (useGithub) {
302
- p3.log.info(pc3.bold("\n\u2500\u2500 GitHub \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500"));
331
+ p4.log.info(pc4.bold("\n\u2500\u2500 GitHub \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500"));
303
332
  if (!isGhInstalled()) {
304
- p3.log.info("The GitHub CLI (gh) is needed to create your repo.");
333
+ p4.log.info("The GitHub CLI (gh) is needed to create your repo.");
305
334
  const installed = await installGh();
306
335
  if (!installed) {
307
- p3.log.warning("Skipping GitHub \u2014 install gh manually: https://cli.github.com");
336
+ p4.log.warning("Skipping GitHub \u2014 install gh manually: https://cli.github.com");
308
337
  }
309
338
  }
310
339
  if (isGhInstalled()) {
311
340
  if (!isGhAuthenticated()) {
312
341
  const loginOk = await ghLogin();
313
342
  if (!loginOk) {
314
- p3.log.warning("GitHub auth failed. Skipping repo creation.");
343
+ p4.log.warning("GitHub auth failed. Skipping repo creation.");
315
344
  }
316
345
  }
317
346
  if (isGhAuthenticated()) {
318
347
  const orgs = getGitHubOrgs();
319
348
  let repoFullName = projectName;
320
349
  if (orgs.length > 0) {
321
- const orgChoice = await p3.select({
350
+ const orgChoice = await p4.select({
322
351
  message: "Which GitHub account?",
323
352
  options: [
324
353
  { value: "", label: "Personal account", hint: "your personal GitHub" },
@@ -329,7 +358,7 @@ async function runDeployPipeline(options) {
329
358
  }))
330
359
  ]
331
360
  });
332
- if (!p3.isCancel(orgChoice) && orgChoice) {
361
+ if (!p4.isCancel(orgChoice) && orgChoice) {
333
362
  repoFullName = `${orgChoice}/${projectName}`;
334
363
  }
335
364
  }
@@ -338,24 +367,24 @@ async function runDeployPipeline(options) {
338
367
  }
339
368
  }
340
369
  if (useVercel) {
341
- p3.log.info(pc3.bold("\n\u2500\u2500 Vercel \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500"));
370
+ p4.log.info(pc4.bold("\n\u2500\u2500 Vercel \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500"));
342
371
  const vercelOk = await installOrUpdateVercel();
343
372
  if (!vercelOk) {
344
- p3.log.error("Could not set up Vercel CLI.");
373
+ p4.log.error("Could not set up Vercel CLI.");
345
374
  return githubRepoUrl ? { productionUrl: "", githubUrl: githubRepoUrl } : null;
346
375
  }
347
376
  if (!isVercelAuthenticated()) {
348
377
  const loginOk = await vercelLogin();
349
378
  if (!loginOk) {
350
- p3.log.error("Vercel auth failed. Run " + pc3.bold("whop-kit deploy") + " later.");
379
+ p4.log.error("Vercel auth failed. Run " + pc4.bold("whop-kit deploy") + " later.");
351
380
  return githubRepoUrl ? { productionUrl: "", githubUrl: githubRepoUrl } : null;
352
381
  }
353
382
  }
354
383
  const vercelUser = getVercelUser();
355
- p3.log.success(`Signed in${vercelUser ? ` as ${pc3.bold(vercelUser)}` : ""}`);
384
+ p4.log.success(`Signed in${vercelUser ? ` as ${pc4.bold(vercelUser)}` : ""}`);
356
385
  await vercelLink(projectDir);
357
386
  if (githubRepoUrl) {
358
- const s = p3.spinner();
387
+ const s = p4.spinner();
359
388
  s.start("Connecting GitHub to Vercel (auto-deploy on push)...");
360
389
  const connectResult = exec(`vercel git connect ${githubRepoUrl}`, projectDir, 3e4);
361
390
  if (connectResult.success) {
@@ -365,40 +394,37 @@ async function runDeployPipeline(options) {
365
394
  }
366
395
  }
367
396
  if (databaseUrl) {
368
- let s = p3.spinner();
397
+ let s = p4.spinner();
369
398
  s.start("Setting DATABASE_URL \u2192 production...");
370
399
  vercelEnvSet("DATABASE_URL", databaseUrl, "production", projectDir);
371
400
  s.stop("DATABASE_URL \u2192 production \u2713");
372
- s = p3.spinner();
401
+ s = p4.spinner();
373
402
  s.start("Setting DATABASE_URL \u2192 preview...");
374
403
  vercelEnvSet("DATABASE_URL", databaseUrl, "preview", projectDir);
375
404
  s.stop("DATABASE_URL \u2192 preview \u2713");
376
- s = p3.spinner();
405
+ s = p4.spinner();
377
406
  s.start("Setting DATABASE_URL \u2192 development...");
378
407
  vercelEnvSet("DATABASE_URL", databaseUrl, "development", projectDir);
379
408
  s.stop("DATABASE_URL \u2192 development \u2713");
380
409
  }
381
410
  productionUrl = await vercelDeploy(projectDir);
382
411
  if (!productionUrl) {
383
- p3.log.error("Deploy failed. Try: " + pc3.bold(`cd ${projectName} && vercel deploy --prod`));
412
+ p4.log.error("Deploy failed. Try: " + pc4.bold(`cd ${projectName} && vercel deploy --prod`));
384
413
  }
385
414
  }
386
415
  if (productionUrl) {
387
- p3.log.info(pc3.bold("\n\u2500\u2500 Whop \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500"));
388
- const connectWhop = await p3.confirm({
416
+ p4.log.info(pc4.bold("\n\u2500\u2500 Whop \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500"));
417
+ const connectWhop = await p4.confirm({
389
418
  message: "Connect to Whop? (creates OAuth app + webhooks automatically)",
390
419
  initialValue: true
391
420
  });
392
- if (!p3.isCancel(connectWhop) && connectWhop) {
393
- p3.note(
421
+ if (!p4.isCancel(connectWhop) && connectWhop) {
422
+ p4.note(
394
423
  [
395
- `We need a Company API key to set up OAuth and webhooks.`,
396
- "",
397
- `${pc3.bold("1.")} Go to ${pc3.cyan("https://whop.com/dashboard/developer")}`,
398
- `${pc3.bold("2.")} Click ${pc3.bold('"Create"')} under "Company API Keys"`,
399
- `${pc3.bold("3.")} Give it a name (e.g. "${projectName}")`,
400
- `${pc3.bold("4.")} Click Create and copy the key`,
401
- `${pc3.bold("5.")} Paste it below`
424
+ `${pc4.bold("1.")} Go to ${pc4.cyan("https://whop.com/dashboard/developer")}`,
425
+ `${pc4.bold("2.")} Under "Company API Keys", click ${pc4.bold('"Create"')}`,
426
+ `${pc4.bold("3.")} Name it (e.g. "${projectName}"), click Create`,
427
+ `${pc4.bold("4.")} Copy the key and paste it below`
402
428
  ].join("\n"),
403
429
  "Whop Company API Key"
404
430
  );
@@ -407,18 +433,18 @@ async function runDeployPipeline(options) {
407
433
  let keyValid = false;
408
434
  for (let attempt = 0; attempt < 3; attempt++) {
409
435
  if (!apiKey) {
410
- const result = await p3.text({
436
+ const result = await p4.text({
411
437
  message: attempt === 0 ? "Paste your Company API key" : "Paste a new Company API key",
412
- placeholder: "paste the key here...",
413
- validate: (v) => !v ? "API key is required" : void 0
438
+ placeholder: "apik_...",
439
+ validate: (v) => !v ? "Required" : void 0
414
440
  });
415
- if (p3.isCancel(result)) {
441
+ if (p4.isCancel(result)) {
416
442
  return { productionUrl, githubUrl: githubRepoUrl ?? void 0 };
417
443
  }
418
444
  apiKey = result;
419
445
  }
420
- const s2 = p3.spinner();
421
- s2.start("Validating API key...");
446
+ let s2 = p4.spinner();
447
+ s2.start("Validating...");
422
448
  keyValid = await validateApiKey(apiKey);
423
449
  if (keyValid) {
424
450
  s2.stop("API key valid");
@@ -426,37 +452,49 @@ async function runDeployPipeline(options) {
426
452
  }
427
453
  s2.stop("API key invalid");
428
454
  if (attempt < 2) {
429
- const retry = await p3.confirm({
430
- message: "Try a different key?",
431
- initialValue: true
432
- });
433
- if (p3.isCancel(retry) || !retry) {
434
- return { productionUrl, githubUrl: githubRepoUrl ?? void 0 };
435
- }
455
+ const retry = await p4.confirm({ message: "Try a different key?", initialValue: true });
456
+ if (p4.isCancel(retry) || !retry) return { productionUrl, githubUrl: githubRepoUrl ?? void 0 };
436
457
  apiKey = "";
437
- } else {
438
- p3.log.error("Could not validate. Configure Whop manually via the setup wizard.");
439
- return { productionUrl, githubUrl: githubRepoUrl ?? void 0 };
440
458
  }
441
459
  }
442
- if (!keyValid) {
443
- return { productionUrl, githubUrl: githubRepoUrl ?? void 0 };
444
- }
460
+ if (!keyValid) return { productionUrl, githubUrl: githubRepoUrl ?? void 0 };
461
+ const companyId = await getCompanyId(apiKey);
462
+ if (!companyId) return { productionUrl, githubUrl: githubRepoUrl ?? void 0 };
445
463
  const redirectUris = [
446
464
  "http://localhost:3000/api/auth/callback",
447
465
  `${productionUrl}/api/auth/callback`
448
466
  ];
449
- const s = p3.spinner();
467
+ let s = p4.spinner();
450
468
  s.start("Creating Whop OAuth app...");
451
- const app = await createWhopApp(apiKey, projectName, redirectUris);
469
+ const app = await createWhopApp(apiKey, projectName, redirectUris, companyId);
452
470
  if (!app) {
453
471
  s.stop("Failed to create app");
454
- p3.log.error("Create manually: " + pc3.cyan("https://whop.com/dashboard/developer"));
472
+ p4.log.error("Create manually in the Whop dashboard.");
455
473
  return { productionUrl, githubUrl: githubRepoUrl ?? void 0 };
456
474
  }
457
- s.stop(`OAuth app created: ${pc3.bold(app.id)}`);
475
+ s.stop(`OAuth app created: ${pc4.bold(app.id)}`);
476
+ p4.note(
477
+ [
478
+ `Your app ${pc4.bold(app.id)} was created. Now get its API key:`,
479
+ "",
480
+ `${pc4.bold("1.")} Go to ${pc4.cyan("https://whop.com/dashboard/developer")}`,
481
+ `${pc4.bold("2.")} Click on your new app "${projectName}"`,
482
+ `${pc4.bold("3.")} Find ${pc4.bold("WHOP_API_KEY")} in the Environment Variables section`,
483
+ `${pc4.bold("4.")} Click reveal, copy it, and paste below`
484
+ ].join("\n"),
485
+ "App API Key"
486
+ );
487
+ openUrl("https://whop.com/dashboard/developer");
488
+ let appApiKey = "";
489
+ const appKeyResult = await p4.text({
490
+ message: "Paste the App API key (WHOP_API_KEY)",
491
+ placeholder: "starts with apik_...",
492
+ validate: (v) => !v ? "Required \u2014 find it in the app's Environment Variables" : void 0
493
+ });
494
+ if (!p4.isCancel(appKeyResult)) appApiKey = appKeyResult;
495
+ s = p4.spinner();
458
496
  s.start("Creating webhook...");
459
- const webhook = await createWhopWebhook(apiKey, `${productionUrl}/api/webhooks/whop`, WEBHOOK_EVENTS);
497
+ const webhook = await createWhopWebhook(apiKey, `${productionUrl}/api/webhooks/whop`, WEBHOOK_EVENTS, companyId);
460
498
  if (!webhook) {
461
499
  s.stop("Failed (create manually in Whop dashboard)");
462
500
  } else {
@@ -469,16 +507,18 @@ async function runDeployPipeline(options) {
469
507
  } else {
470
508
  envVars["WHOP_APP_ID"] = app.id;
471
509
  }
472
- envVars["WHOP_API_KEY"] = app.client_secret;
510
+ if (appApiKey) envVars["WHOP_API_KEY"] = appApiKey;
473
511
  if (webhook?.secret) envVars["WHOP_WEBHOOK_SECRET"] = webhook.secret;
474
512
  for (const [key, value] of Object.entries(envVars)) {
475
- const vs = p3.spinner();
513
+ if (!value) continue;
514
+ const vs = p4.spinner();
476
515
  vs.start(`Pushing ${key}...`);
477
516
  vercelEnvSet(key, value, "production", projectDir);
478
517
  vercelEnvSet(key, value, "preview", projectDir);
479
518
  vercelEnvSet(key, value, "development", projectDir);
480
519
  vs.stop(`${key} \u2713`);
481
520
  }
521
+ s = p4.spinner();
482
522
  s.start("Redeploying with full configuration...");
483
523
  const redeploy = exec("vercel deploy --prod --yes", projectDir, 3e5);
484
524
  s.stop(redeploy.success ? "Redeployed" : "Redeploy pending \u2014 will apply on next git push");
@@ -487,7 +527,7 @@ async function runDeployPipeline(options) {
487
527
  productionUrl,
488
528
  githubUrl: githubRepoUrl ?? void 0,
489
529
  whopAppId: app.id,
490
- whopApiKey: app.client_secret,
530
+ whopApiKey: appApiKey || void 0,
491
531
  webhookSecret: webhook?.secret
492
532
  };
493
533
  }
@@ -684,7 +684,7 @@ var init_default = defineCommand({
684
684
  });
685
685
  if (!isCancelled(deployChoice) && deployChoice === "deploy") {
686
686
  deployAttempted = true;
687
- const { runDeployPipeline } = await import("./deploy-AMH7INFV.js");
687
+ const { runDeployPipeline } = await import("./deploy-HMCWU54H.js");
688
688
  deployResult = await runDeployPipeline({
689
689
  projectDir,
690
690
  projectName,
package/dist/cli-kit.js CHANGED
@@ -9,7 +9,7 @@ import {
9
9
  } from "./chunk-HOQ5QQ2M.js";
10
10
  import {
11
11
  runDeployPipeline
12
- } from "./chunk-E2IWGU3U.js";
12
+ } from "./chunk-USEGOWYX.js";
13
13
  import {
14
14
  detectPackageManager,
15
15
  exec
@@ -1,7 +1,7 @@
1
1
  #!/usr/bin/env node
2
2
  import {
3
3
  runDeployPipeline
4
- } from "./chunk-E2IWGU3U.js";
4
+ } from "./chunk-USEGOWYX.js";
5
5
  import "./chunk-42L7PRMT.js";
6
6
  export {
7
7
  runDeployPipeline
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "create-whop-kit",
3
- "version": "1.0.7",
3
+ "version": "1.0.8",
4
4
  "description": "Scaffold and manage Whop-powered apps with whop-kit",
5
5
  "type": "module",
6
6
  "license": "MIT",