create-whop-kit 1.0.6 → 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";
@@ -17,32 +17,32 @@ function isVercelInstalled() {
17
17
  return hasCommand("vercel");
18
18
  }
19
19
  async function installOrUpdateVercel() {
20
- const s2 = p.spinner();
20
+ const s = p.spinner();
21
21
  if (isVercelInstalled()) {
22
22
  const versionResult = exec("vercel --version");
23
23
  const currentVersion = versionResult.stdout.replace(/[^0-9.]/g, "");
24
- s2.start("Checking for Vercel CLI updates...");
24
+ s.start("Checking for Vercel CLI updates...");
25
25
  const updateResult = exec("npm install -g vercel@latest", void 0, 6e4);
26
26
  if (updateResult.success) {
27
27
  const newVersion = exec("vercel --version");
28
28
  const newVer = newVersion.stdout.replace(/[^0-9.]/g, "");
29
29
  if (newVer !== currentVersion) {
30
- s2.stop(`Vercel CLI updated: ${currentVersion} \u2192 ${newVer}`);
30
+ s.stop(`Vercel CLI updated: ${currentVersion} \u2192 ${newVer}`);
31
31
  } else {
32
- s2.stop(`Vercel CLI up to date (v${currentVersion})`);
32
+ s.stop(`Vercel CLI up to date (v${currentVersion})`);
33
33
  }
34
34
  } else {
35
- s2.stop(`Vercel CLI v${currentVersion} (update check failed, continuing)`);
35
+ s.stop(`Vercel CLI v${currentVersion} (update check failed, continuing)`);
36
36
  }
37
37
  return true;
38
38
  }
39
- s2.start("Installing Vercel CLI...");
39
+ s.start("Installing Vercel CLI...");
40
40
  const result = exec("npm install -g vercel@latest");
41
41
  if (result.success) {
42
- s2.stop("Vercel CLI installed");
42
+ s.stop("Vercel CLI installed");
43
43
  return true;
44
44
  }
45
- s2.stop("Failed to install Vercel CLI");
45
+ s.stop("Failed to install Vercel CLI");
46
46
  p.log.error(`Install manually: ${pc.bold("npm install -g vercel@latest")}`);
47
47
  return false;
48
48
  }
@@ -121,14 +121,14 @@ function isGhAuthenticated() {
121
121
  return result.success;
122
122
  }
123
123
  async function installGh() {
124
- const s2 = p2.spinner();
125
- s2.start("Installing GitHub CLI...");
124
+ const s = p2.spinner();
125
+ s.start("Installing GitHub CLI...");
126
126
  const result = exec("npm install -g gh", void 0, 6e4);
127
127
  if (result.success && hasCommand("gh")) {
128
- s2.stop("GitHub CLI installed");
128
+ s.stop("GitHub CLI installed");
129
129
  return true;
130
130
  }
131
- s2.stop("Could not auto-install GitHub CLI");
131
+ s.stop("Could not auto-install GitHub CLI");
132
132
  p2.log.info("Install manually:");
133
133
  p2.log.info(pc2.bold(" https://cli.github.com"));
134
134
  return false;
@@ -141,8 +141,8 @@ async function ghLogin() {
141
141
  return ok;
142
142
  }
143
143
  async function createGitHubRepo(projectDir, projectName) {
144
- let s2 = p2.spinner();
145
- s2.start("Creating private GitHub repository...");
144
+ let s = p2.spinner();
145
+ s.start("Creating private GitHub repository...");
146
146
  const createResult = exec(
147
147
  `gh repo create ${projectName} --private --source=.`,
148
148
  projectDir,
@@ -151,35 +151,35 @@ async function createGitHubRepo(projectDir, projectName) {
151
151
  if (!createResult.success) {
152
152
  const stderr = createResult.stderr || createResult.stdout;
153
153
  if (stderr.includes("already exists")) {
154
- s2.stop(`Repository "${projectName}" already exists`);
154
+ s.stop(`Repository "${projectName}" already exists`);
155
155
  exec(`git remote add origin https://github.com/$(gh api user --jq .login)/${projectName}.git`, projectDir);
156
156
  } else {
157
- s2.stop("Could not create GitHub repo");
157
+ s.stop("Could not create GitHub repo");
158
158
  if (stderr) p2.log.error(pc2.dim(stderr.substring(0, 200)));
159
159
  return null;
160
160
  }
161
161
  } else {
162
- s2.stop("GitHub repo created");
162
+ s.stop("GitHub repo created");
163
163
  }
164
- s2 = p2.spinner();
165
- s2.start("Pushing code to GitHub...");
164
+ s = p2.spinner();
165
+ s.start("Pushing code to GitHub...");
166
166
  const branchResult = exec("git branch --show-current", projectDir);
167
167
  const branch = branchResult.success ? branchResult.stdout.trim() : "main";
168
168
  let pushOk = false;
169
169
  for (const delay of [0, 3e3, 7e3]) {
170
170
  if (delay > 0) {
171
- s2.stop(`Waiting for GitHub to propagate...`);
171
+ s.stop(`Waiting for GitHub to propagate...`);
172
172
  await new Promise((r) => setTimeout(r, delay));
173
- s2 = p2.spinner();
174
- s2.start("Pushing code to GitHub...");
173
+ s = p2.spinner();
174
+ s.start("Pushing code to GitHub...");
175
175
  }
176
176
  pushOk = exec(`git push -u origin ${branch}`, projectDir, 3e4).success;
177
177
  if (pushOk) break;
178
178
  }
179
179
  if (!pushOk) {
180
- s2.stop(`Could not push (try manually: git push -u origin ${branch})`);
180
+ s.stop(`Could not push (try manually: git push -u origin ${branch})`);
181
181
  } else {
182
- s2.stop("Code pushed to GitHub");
182
+ s.stop("Code pushed to GitHub");
183
183
  }
184
184
  const repoUrl = exec("gh repo view --json url --jq .url", projectDir);
185
185
  if (repoUrl.success && repoUrl.stdout.trim()) {
@@ -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 {
@@ -201,24 +203,41 @@ function headers(apiKey) {
201
203
  "Content-Type": "application/json"
202
204
  };
203
205
  }
206
+ async function validateApiKey(apiKey) {
207
+ try {
208
+ const res = await fetch(`${WHOP_API}/apps?per_page=1`, {
209
+ headers: headers(apiKey)
210
+ });
211
+ return res.ok;
212
+ } catch {
213
+ return false;
214
+ }
215
+ }
204
216
  async function getCompanyId(apiKey) {
205
217
  try {
206
218
  const res = await fetch(`${WHOP_API}/companies`, {
207
219
  headers: headers(apiKey)
208
220
  });
209
- if (!res.ok) return null;
210
- const data = await res.json();
211
- const companies = data.data || data;
212
- if (Array.isArray(companies) && companies.length > 0) {
213
- return companies[0].id;
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
+ }
214
227
  }
215
- return null;
216
228
  } catch {
217
- return null;
218
229
  }
219
- }
220
- async function validateApiKey(apiKey) {
221
- return getCompanyId(apiKey);
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;
222
241
  }
223
242
  async function createWhopApp(apiKey, name, redirectUris, companyId) {
224
243
  try {
@@ -231,16 +250,13 @@ async function createWhopApp(apiKey, name, redirectUris, companyId) {
231
250
  redirect_uris: redirectUris
232
251
  })
233
252
  });
234
- if (!res.ok) {
235
- const err = await res.text().catch(() => "");
236
- console.error(`[Whop API] Create app failed (${res.status}): ${err}`);
237
- return null;
253
+ if (res.ok) {
254
+ const data = await res.json();
255
+ return { id: data.id, client_secret: data.client_secret };
238
256
  }
239
- const data = await res.json();
240
- return {
241
- id: data.id,
242
- client_secret: data.client_secret
243
- };
257
+ const err = await res.text().catch(() => "");
258
+ console.error(`[Whop API] Create app failed (${res.status}): ${err}`);
259
+ return null;
244
260
  } catch (err) {
245
261
  console.error("[Whop API] Create app error:", err);
246
262
  return null;
@@ -251,22 +267,18 @@ async function createWhopWebhook(apiKey, url, events, companyId) {
251
267
  const res = await fetch(`${WHOP_API}/webhooks`, {
252
268
  method: "POST",
253
269
  headers: headers(apiKey),
254
- body: JSON.stringify({
255
- url,
256
- events,
257
- company_id: companyId
258
- })
270
+ body: JSON.stringify({ url, events, company_id: companyId })
259
271
  });
260
- if (!res.ok) {
261
- const err = await res.text().catch(() => "");
262
- console.error(`[Whop API] Create webhook failed (${res.status}): ${err}`);
263
- return null;
272
+ if (res.ok) {
273
+ const data = await res.json();
274
+ return {
275
+ id: data.id,
276
+ secret: data.secret || data.signing_secret || data.webhook_secret || ""
277
+ };
264
278
  }
265
- const data = await res.json();
266
- return {
267
- id: data.id,
268
- secret: data.secret || data.signing_secret || data.webhook_secret || ""
269
- };
279
+ const err = await res.text().catch(() => "");
280
+ console.error(`[Whop API] Create webhook failed (${res.status}): ${err}`);
281
+ return null;
270
282
  } catch (err) {
271
283
  console.error("[Whop API] Create webhook error:", err);
272
284
  return null;
@@ -290,7 +302,7 @@ function openUrl(url) {
290
302
  }
291
303
  async function runDeployPipeline(options) {
292
304
  const { projectDir, projectName, databaseUrl, framework } = options;
293
- const setupMode = await p3.select({
305
+ const setupMode = await p4.select({
294
306
  message: "How would you like to deploy?",
295
307
  options: [
296
308
  {
@@ -310,32 +322,32 @@ async function runDeployPipeline(options) {
310
322
  }
311
323
  ]
312
324
  });
313
- if (p3.isCancel(setupMode)) return null;
325
+ if (p4.isCancel(setupMode)) return null;
314
326
  const useGithub = setupMode === "github-vercel" || setupMode === "github-only";
315
327
  const useVercel = setupMode === "github-vercel" || setupMode === "vercel-only";
316
328
  let githubRepoUrl = null;
317
329
  let productionUrl = null;
318
330
  if (useGithub) {
319
- 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"));
320
332
  if (!isGhInstalled()) {
321
- 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.");
322
334
  const installed = await installGh();
323
335
  if (!installed) {
324
- 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");
325
337
  }
326
338
  }
327
339
  if (isGhInstalled()) {
328
340
  if (!isGhAuthenticated()) {
329
341
  const loginOk = await ghLogin();
330
342
  if (!loginOk) {
331
- p3.log.warning("GitHub auth failed. Skipping repo creation.");
343
+ p4.log.warning("GitHub auth failed. Skipping repo creation.");
332
344
  }
333
345
  }
334
346
  if (isGhAuthenticated()) {
335
347
  const orgs = getGitHubOrgs();
336
348
  let repoFullName = projectName;
337
349
  if (orgs.length > 0) {
338
- const orgChoice = await p3.select({
350
+ const orgChoice = await p4.select({
339
351
  message: "Which GitHub account?",
340
352
  options: [
341
353
  { value: "", label: "Personal account", hint: "your personal GitHub" },
@@ -346,7 +358,7 @@ async function runDeployPipeline(options) {
346
358
  }))
347
359
  ]
348
360
  });
349
- if (!p3.isCancel(orgChoice) && orgChoice) {
361
+ if (!p4.isCancel(orgChoice) && orgChoice) {
350
362
  repoFullName = `${orgChoice}/${projectName}`;
351
363
  }
352
364
  }
@@ -355,123 +367,132 @@ async function runDeployPipeline(options) {
355
367
  }
356
368
  }
357
369
  if (useVercel) {
358
- 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"));
359
371
  const vercelOk = await installOrUpdateVercel();
360
372
  if (!vercelOk) {
361
- p3.log.error("Could not set up Vercel CLI.");
373
+ p4.log.error("Could not set up Vercel CLI.");
362
374
  return githubRepoUrl ? { productionUrl: "", githubUrl: githubRepoUrl } : null;
363
375
  }
364
376
  if (!isVercelAuthenticated()) {
365
377
  const loginOk = await vercelLogin();
366
378
  if (!loginOk) {
367
- 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.");
368
380
  return githubRepoUrl ? { productionUrl: "", githubUrl: githubRepoUrl } : null;
369
381
  }
370
382
  }
371
383
  const vercelUser = getVercelUser();
372
- p3.log.success(`Signed in${vercelUser ? ` as ${pc3.bold(vercelUser)}` : ""}`);
384
+ p4.log.success(`Signed in${vercelUser ? ` as ${pc4.bold(vercelUser)}` : ""}`);
373
385
  await vercelLink(projectDir);
374
386
  if (githubRepoUrl) {
375
- const s2 = p3.spinner();
376
- s2.start("Connecting GitHub to Vercel (auto-deploy on push)...");
387
+ const s = p4.spinner();
388
+ s.start("Connecting GitHub to Vercel (auto-deploy on push)...");
377
389
  const connectResult = exec(`vercel git connect ${githubRepoUrl}`, projectDir, 3e4);
378
390
  if (connectResult.success) {
379
- s2.stop("Connected \u2014 every git push will auto-deploy");
391
+ s.stop("Connected \u2014 every git push will auto-deploy");
380
392
  } else {
381
- s2.stop("Auto-connect failed (connect manually in Vercel dashboard \u2192 Git)");
393
+ s.stop("Auto-connect failed (connect manually in Vercel dashboard \u2192 Git)");
382
394
  }
383
395
  }
384
396
  if (databaseUrl) {
385
- let s2 = p3.spinner();
386
- s2.start("Setting DATABASE_URL \u2192 production...");
397
+ let s = p4.spinner();
398
+ s.start("Setting DATABASE_URL \u2192 production...");
387
399
  vercelEnvSet("DATABASE_URL", databaseUrl, "production", projectDir);
388
- s2.stop("DATABASE_URL \u2192 production \u2713");
389
- s2 = p3.spinner();
390
- s2.start("Setting DATABASE_URL \u2192 preview...");
400
+ s.stop("DATABASE_URL \u2192 production \u2713");
401
+ s = p4.spinner();
402
+ s.start("Setting DATABASE_URL \u2192 preview...");
391
403
  vercelEnvSet("DATABASE_URL", databaseUrl, "preview", projectDir);
392
- s2.stop("DATABASE_URL \u2192 preview \u2713");
393
- s2 = p3.spinner();
394
- s2.start("Setting DATABASE_URL \u2192 development...");
404
+ s.stop("DATABASE_URL \u2192 preview \u2713");
405
+ s = p4.spinner();
406
+ s.start("Setting DATABASE_URL \u2192 development...");
395
407
  vercelEnvSet("DATABASE_URL", databaseUrl, "development", projectDir);
396
- s2.stop("DATABASE_URL \u2192 development \u2713");
408
+ s.stop("DATABASE_URL \u2192 development \u2713");
397
409
  }
398
410
  productionUrl = await vercelDeploy(projectDir);
399
411
  if (!productionUrl) {
400
- 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`));
401
413
  }
402
414
  }
403
415
  if (productionUrl) {
404
- 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"));
405
- 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({
406
418
  message: "Connect to Whop? (creates OAuth app + webhooks automatically)",
407
419
  initialValue: true
408
420
  });
409
- if (!p3.isCancel(connectWhop) && connectWhop) {
410
- p3.note(
421
+ if (!p4.isCancel(connectWhop) && connectWhop) {
422
+ p4.note(
411
423
  [
412
- `We need a Company API key to set up OAuth and webhooks.`,
413
- "",
414
- `${pc3.bold("1.")} Go to ${pc3.cyan("https://whop.com/dashboard/developer")}`,
415
- `${pc3.bold("2.")} Click ${pc3.bold('"Create"')} under "Company API Keys"`,
416
- `${pc3.bold("3.")} Give it a name (e.g. "${projectName}")`,
417
- `${pc3.bold("4.")} Set role to ${pc3.bold('"Owner"')} ${pc3.dim("(ensures all permissions)")}`,
418
- `${pc3.bold("5.")} Click Create, copy the key, and 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`
419
428
  ].join("\n"),
420
429
  "Whop Company API Key"
421
430
  );
422
431
  openUrl("https://whop.com/dashboard/developer");
423
432
  let apiKey = options.whopCompanyKey ?? "";
424
- let companyId = null;
433
+ let keyValid = false;
425
434
  for (let attempt = 0; attempt < 3; attempt++) {
426
435
  if (!apiKey) {
427
- const result = await p3.text({
428
- message: attempt === 0 ? "Paste your Company API key" : "Paste a new Company API key (make sure role is set to Owner)",
429
- placeholder: "paste the key here...",
430
- validate: (v) => !v ? "API key is required" : void 0
436
+ const result = await p4.text({
437
+ message: attempt === 0 ? "Paste your Company API key" : "Paste a new Company API key",
438
+ placeholder: "apik_...",
439
+ validate: (v) => !v ? "Required" : void 0
431
440
  });
432
- if (p3.isCancel(result)) {
441
+ if (p4.isCancel(result)) {
433
442
  return { productionUrl, githubUrl: githubRepoUrl ?? void 0 };
434
443
  }
435
444
  apiKey = result;
436
445
  }
437
- const s2 = p3.spinner();
438
- s2.start("Validating API key...");
439
- companyId = await validateApiKey(apiKey);
440
- if (companyId) {
441
- s2.stop(`API key valid (company: ${pc3.dim(companyId)})`);
446
+ let s2 = p4.spinner();
447
+ s2.start("Validating...");
448
+ keyValid = await validateApiKey(apiKey);
449
+ if (keyValid) {
450
+ s2.stop("API key valid");
442
451
  break;
443
452
  }
444
- s2.stop("API key invalid or missing permissions");
453
+ s2.stop("API key invalid");
445
454
  if (attempt < 2) {
446
- p3.log.warning(`Make sure the key's role is set to "Owner" (not Admin).`);
447
- const retry = await p3.confirm({
448
- message: "Try a different key?",
449
- initialValue: true
450
- });
451
- if (p3.isCancel(retry) || !retry) {
452
- return { productionUrl, githubUrl: githubRepoUrl ?? void 0 };
453
- }
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 };
454
457
  apiKey = "";
455
- } else {
456
- p3.log.error("Could not validate after 3 attempts. Configure Whop manually via the setup wizard.");
457
- return { productionUrl, githubUrl: githubRepoUrl ?? void 0 };
458
458
  }
459
459
  }
460
- if (!companyId) {
461
- return { productionUrl, githubUrl: githubRepoUrl ?? void 0 };
462
- }
460
+ if (!keyValid) return { productionUrl, githubUrl: githubRepoUrl ?? void 0 };
461
+ const companyId = await getCompanyId(apiKey);
462
+ if (!companyId) return { productionUrl, githubUrl: githubRepoUrl ?? void 0 };
463
463
  const redirectUris = [
464
464
  "http://localhost:3000/api/auth/callback",
465
465
  `${productionUrl}/api/auth/callback`
466
466
  ];
467
+ let s = p4.spinner();
467
468
  s.start("Creating Whop OAuth app...");
468
469
  const app = await createWhopApp(apiKey, projectName, redirectUris, companyId);
469
470
  if (!app) {
470
471
  s.stop("Failed to create app");
471
- p3.log.error("Create manually: " + pc3.cyan("https://whop.com/dashboard/developer"));
472
+ p4.log.error("Create manually in the Whop dashboard.");
472
473
  return { productionUrl, githubUrl: githubRepoUrl ?? void 0 };
473
474
  }
474
- 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();
475
496
  s.start("Creating webhook...");
476
497
  const webhook = await createWhopWebhook(apiKey, `${productionUrl}/api/webhooks/whop`, WEBHOOK_EVENTS, companyId);
477
498
  if (!webhook) {
@@ -486,16 +507,18 @@ async function runDeployPipeline(options) {
486
507
  } else {
487
508
  envVars["WHOP_APP_ID"] = app.id;
488
509
  }
489
- envVars["WHOP_API_KEY"] = app.client_secret;
510
+ if (appApiKey) envVars["WHOP_API_KEY"] = appApiKey;
490
511
  if (webhook?.secret) envVars["WHOP_WEBHOOK_SECRET"] = webhook.secret;
491
512
  for (const [key, value] of Object.entries(envVars)) {
492
- const vs = p3.spinner();
513
+ if (!value) continue;
514
+ const vs = p4.spinner();
493
515
  vs.start(`Pushing ${key}...`);
494
516
  vercelEnvSet(key, value, "production", projectDir);
495
517
  vercelEnvSet(key, value, "preview", projectDir);
496
518
  vercelEnvSet(key, value, "development", projectDir);
497
519
  vs.stop(`${key} \u2713`);
498
520
  }
521
+ s = p4.spinner();
499
522
  s.start("Redeploying with full configuration...");
500
523
  const redeploy = exec("vercel deploy --prod --yes", projectDir, 3e5);
501
524
  s.stop(redeploy.success ? "Redeployed" : "Redeploy pending \u2014 will apply on next git push");
@@ -504,7 +527,7 @@ async function runDeployPipeline(options) {
504
527
  productionUrl,
505
528
  githubUrl: githubRepoUrl ?? void 0,
506
529
  whopAppId: app.id,
507
- whopApiKey: app.client_secret,
530
+ whopApiKey: appApiKey || void 0,
508
531
  webhookSecret: webhook?.secret
509
532
  };
510
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-P3HDSFDE.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-XTFE7H37.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-XTFE7H37.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.6",
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",