create-whop-kit 0.7.0 → 0.8.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.
@@ -7,8 +7,8 @@ import {
7
7
  } from "./chunk-42L7PRMT.js";
8
8
 
9
9
  // src/deploy/index.ts
10
- import * as p2 from "@clack/prompts";
11
- import pc2 from "picocolors";
10
+ import * as p3 from "@clack/prompts";
11
+ import pc3 from "picocolors";
12
12
 
13
13
  // src/deploy/vercel.ts
14
14
  import * as p from "@clack/prompts";
@@ -147,6 +147,67 @@ function vercelEnvSetBatch(vars, projectDir) {
147
147
  return { success, failed };
148
148
  }
149
149
 
150
+ // src/deploy/github.ts
151
+ import * as p2 from "@clack/prompts";
152
+ import pc2 from "picocolors";
153
+ function isGhInstalled() {
154
+ return hasCommand("gh");
155
+ }
156
+ function isGhAuthenticated() {
157
+ const result = exec("gh auth status");
158
+ return result.success;
159
+ }
160
+ async function installGh() {
161
+ const s = p2.spinner();
162
+ s.start("Installing GitHub CLI...");
163
+ const result = exec("npm install -g gh", void 0, 6e4);
164
+ if (result.success && hasCommand("gh")) {
165
+ s.stop("GitHub CLI installed");
166
+ return true;
167
+ }
168
+ s.stop("Could not auto-install GitHub CLI");
169
+ p2.log.info("Install manually:");
170
+ p2.log.info(pc2.bold(" https://cli.github.com"));
171
+ return false;
172
+ }
173
+ async function ghLogin() {
174
+ p2.log.info("You'll be redirected to GitHub to sign in.");
175
+ console.log("");
176
+ const ok = execInteractive("gh auth login --web");
177
+ console.log("");
178
+ return ok;
179
+ }
180
+ async function createGitHubRepo(projectDir, projectName) {
181
+ const s = p2.spinner();
182
+ s.start("Creating private GitHub repository...");
183
+ const result = exec(
184
+ `gh repo create ${projectName} --private --source=. --push`,
185
+ projectDir,
186
+ 6e4
187
+ );
188
+ if (!result.success) {
189
+ s.stop("Could not create repo");
190
+ const stderr = result.stderr || result.stdout;
191
+ if (stderr.includes("already exists")) {
192
+ p2.log.warning(`Repository "${projectName}" already exists on GitHub.`);
193
+ exec(`git remote add origin https://github.com/$(gh api user --jq .login)/${projectName}.git`, projectDir);
194
+ const pushResult = exec("git push -u origin main", projectDir, 3e4);
195
+ if (pushResult.success) {
196
+ const remote = exec("gh repo view --json url --jq .url", projectDir);
197
+ return remote.success ? remote.stdout.trim() : null;
198
+ }
199
+ }
200
+ return null;
201
+ }
202
+ const repoUrl = exec("gh repo view --json url --jq .url", projectDir);
203
+ if (repoUrl.success) {
204
+ s.stop(`GitHub repo created: ${pc2.cyan(repoUrl.stdout.trim())}`);
205
+ return repoUrl.stdout.trim();
206
+ }
207
+ s.stop("GitHub repo created");
208
+ return `https://github.com/${projectName}`;
209
+ }
210
+
150
211
  // src/deploy/whop-api.ts
151
212
  var WHOP_API = "https://api.whop.com/api/v1";
152
213
  function headers(apiKey) {
@@ -233,84 +294,125 @@ function openUrl(url) {
233
294
  }
234
295
  async function runDeployPipeline(options) {
235
296
  const { projectDir, projectName, databaseUrl, framework } = options;
236
- const ok = await installOrUpdateVercel();
237
- if (!ok) return null;
297
+ 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"));
298
+ if (!isGhInstalled()) {
299
+ const install = await p3.confirm({
300
+ message: "GitHub CLI (gh) not found. Install it?",
301
+ initialValue: true
302
+ });
303
+ if (p3.isCancel(install) || !install) {
304
+ p3.log.warning("Skipping GitHub. Deploy will upload directly (no auto-deploy on push).");
305
+ } else {
306
+ await installGh();
307
+ }
308
+ }
309
+ let githubRepoUrl = null;
310
+ if (isGhInstalled()) {
311
+ if (!isGhAuthenticated()) {
312
+ const loginOk = await ghLogin();
313
+ if (!loginOk) {
314
+ p3.log.warning("GitHub auth failed. Skipping GitHub repo creation.");
315
+ }
316
+ }
317
+ if (isGhAuthenticated()) {
318
+ githubRepoUrl = await createGitHubRepo(projectDir, projectName);
319
+ if (githubRepoUrl) {
320
+ p3.log.success(`Code pushed to ${pc3.cyan(githubRepoUrl)}`);
321
+ } else {
322
+ p3.log.warning("Could not create GitHub repo. Continuing without it.");
323
+ }
324
+ }
325
+ }
326
+ 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"));
327
+ const vercelOk = await installOrUpdateVercel();
328
+ if (!vercelOk) return null;
238
329
  if (!isVercelAuthenticated()) {
239
330
  const loginOk = await vercelLogin();
240
331
  if (!loginOk) {
241
- p2.log.error("Vercel authentication failed. Deploy later with: " + pc2.bold("whop-kit deploy"));
332
+ p3.log.error("Vercel auth failed. Run " + pc3.bold("whop-kit deploy") + " later.");
242
333
  return null;
243
334
  }
244
335
  }
245
- const user = getVercelUser();
246
- p2.log.success(`Vercel authenticated${user ? ` as ${pc2.bold(user)}` : ""}`);
336
+ const vercelUser = getVercelUser();
337
+ p3.log.success(`Signed in${vercelUser ? ` as ${pc3.bold(vercelUser)}` : ""}`);
247
338
  const linkOk = await vercelLink(projectDir);
248
339
  if (!linkOk) {
249
- p2.log.warning("Could not link project. Will try deploying directly.");
340
+ p3.log.warning("Could not link project.");
341
+ }
342
+ if (githubRepoUrl) {
343
+ const s2 = p3.spinner();
344
+ s2.start("Connecting GitHub repo to Vercel (enables auto-deploy on push)...");
345
+ const connectResult = exec(
346
+ `vercel git connect ${githubRepoUrl}`,
347
+ projectDir,
348
+ 3e4
349
+ );
350
+ if (connectResult.success) {
351
+ s2.stop("GitHub connected \u2014 future pushes will auto-deploy");
352
+ } else {
353
+ s2.stop("Could not auto-connect GitHub (connect manually in Vercel dashboard)");
354
+ }
250
355
  }
251
356
  if (databaseUrl) {
252
- const s2 = p2.spinner();
253
- s2.start("Vercel: setting DATABASE_URL...");
357
+ const s2 = p3.spinner();
358
+ s2.start("Setting DATABASE_URL on Vercel...");
254
359
  vercelEnvSet("DATABASE_URL", databaseUrl, "production", projectDir);
255
360
  vercelEnvSet("DATABASE_URL", databaseUrl, "preview", projectDir);
256
361
  vercelEnvSet("DATABASE_URL", databaseUrl, "development", projectDir);
257
- s2.stop("DATABASE_URL set on Vercel");
362
+ s2.stop("DATABASE_URL configured");
258
363
  }
259
364
  const productionUrl = await vercelDeploy(projectDir);
260
365
  if (!productionUrl) {
261
- p2.log.error("Vercel deployment failed. Try deploying manually:");
262
- p2.log.info(pc2.bold(` cd ${projectName} && vercel deploy --prod`));
366
+ p3.log.error("Deployment failed. Try: " + pc3.bold(`cd ${projectName} && vercel deploy --prod`));
263
367
  return null;
264
368
  }
265
- const connectWhop = await p2.confirm({
369
+ 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"));
370
+ const connectWhop = await p3.confirm({
266
371
  message: "Connect to Whop? (creates OAuth app + webhooks automatically)",
267
372
  initialValue: true
268
373
  });
269
- if (p2.isCancel(connectWhop) || !connectWhop) {
374
+ if (p3.isCancel(connectWhop) || !connectWhop) {
270
375
  return { productionUrl };
271
376
  }
272
- p2.log.info("");
273
- p2.note(
377
+ p3.note(
274
378
  [
275
- `${pc2.bold("1.")} Go to the Whop Developer Dashboard`,
276
- ` ${pc2.cyan("https://whop.com/dashboard/developer")}`,
379
+ `${pc3.bold("1.")} Go to the Whop Developer Dashboard`,
380
+ ` ${pc3.cyan("https://whop.com/dashboard/developer")}`,
277
381
  "",
278
- `${pc2.bold("2.")} Click ${pc2.bold('"Create"')} under "Company API Keys"`,
382
+ `${pc3.bold("2.")} Click ${pc3.bold('"Create"')} under "Company API Keys"`,
279
383
  "",
280
- `${pc2.bold("3.")} Name it anything (e.g. "${projectName}")`,
384
+ `${pc3.bold("3.")} Name it anything (e.g. "${projectName}")`,
281
385
  "",
282
- `${pc2.bold("4.")} Select these permissions:`,
283
- ` ${pc2.green("\u2022")} developer:create_app`,
284
- ` ${pc2.green("\u2022")} developer:manage_api_key`,
285
- ` ${pc2.green("\u2022")} developer:manage_webhook`,
386
+ `${pc3.bold("4.")} Select these permissions:`,
387
+ ` ${pc3.green("\u2022")} developer:create_app`,
388
+ ` ${pc3.green("\u2022")} developer:manage_api_key`,
389
+ ` ${pc3.green("\u2022")} developer:manage_webhook`,
286
390
  "",
287
- `${pc2.bold("5.")} Create the key and paste it below`
391
+ `${pc3.bold("5.")} Create the key and paste it below`
288
392
  ].join("\n"),
289
393
  "Create a Company API Key"
290
394
  );
291
395
  openUrl("https://whop.com/dashboard/developer");
292
396
  let apiKey = options.whopCompanyKey ?? "";
293
397
  if (!apiKey) {
294
- const result = await p2.text({
398
+ const result = await p3.text({
295
399
  message: "Paste your Company API key",
296
400
  placeholder: "paste the key here...",
297
401
  validate: (v) => !v ? "API key is required" : void 0
298
402
  });
299
- if (p2.isCancel(result)) return { productionUrl };
403
+ if (p3.isCancel(result)) return { productionUrl };
300
404
  apiKey = result;
301
405
  }
302
- const s = p2.spinner();
406
+ const s = p3.spinner();
303
407
  s.start("Validating API key...");
304
408
  const keyValid = await validateApiKey(apiKey);
305
409
  if (!keyValid) {
306
410
  s.stop("Invalid API key");
307
- p2.log.error("The key was rejected. Check that it has the required permissions:");
308
- p2.log.info(" developer:create_app, developer:manage_api_key, developer:manage_webhook");
309
- p2.log.info(` Dashboard: ${pc2.cyan("https://whop.com/dashboard/developer")}`);
411
+ p3.log.error("Check permissions: developer:create_app, developer:manage_api_key, developer:manage_webhook");
310
412
  return { productionUrl };
311
413
  }
312
414
  s.stop("API key valid");
313
- const callbackPath = framework === "astro" ? "/api/auth/callback" : "/api/auth/callback";
415
+ const callbackPath = "/api/auth/callback";
314
416
  const redirectUris = [
315
417
  `http://localhost:3000${callbackPath}`,
316
418
  `${productionUrl}${callbackPath}`
@@ -318,19 +420,19 @@ async function runDeployPipeline(options) {
318
420
  s.start("Creating Whop OAuth app...");
319
421
  const app = await createWhopApp(apiKey, projectName, redirectUris);
320
422
  if (!app) {
321
- s.stop("Failed to create Whop app");
322
- p2.log.error("Create it manually in the Whop dashboard.");
423
+ s.stop("Failed to create app");
424
+ p3.log.error("Create manually at: " + pc3.cyan("https://whop.com/dashboard/developer"));
323
425
  return { productionUrl };
324
426
  }
325
- s.stop(`Whop app created: ${pc2.bold(app.id)}`);
427
+ s.stop(`OAuth app created: ${pc3.bold(app.id)}`);
326
428
  const webhookUrl = `${productionUrl}/api/webhooks/whop`;
327
429
  s.start("Creating webhook endpoint...");
328
430
  const webhook = await createWhopWebhook(apiKey, webhookUrl, WEBHOOK_EVENTS);
329
431
  if (!webhook) {
330
432
  s.stop("Failed to create webhook");
331
- p2.log.warning("Create it manually in the Whop dashboard.");
433
+ p3.log.warning("Create manually in the Whop dashboard.");
332
434
  } else {
333
- s.stop("Webhook endpoint created");
435
+ s.stop("Webhook created");
334
436
  }
335
437
  const envVars = {};
336
438
  if (framework === "nextjs") {
@@ -342,22 +444,19 @@ async function runDeployPipeline(options) {
342
444
  if (webhook?.secret) {
343
445
  envVars["WHOP_WEBHOOK_SECRET"] = webhook.secret;
344
446
  }
345
- s.start("Pushing credentials to Vercel...");
447
+ s.start("Pushing Whop credentials to Vercel...");
346
448
  const { success, failed } = vercelEnvSetBatch(envVars, projectDir);
347
449
  if (failed.length > 0) {
348
- s.stop(`Pushed ${success.length} vars, ${failed.length} failed`);
349
- p2.log.warning(`Failed to push: ${failed.join(", ")}. Add them manually in Vercel dashboard.`);
450
+ s.stop(`${success.length} pushed, ${failed.length} failed`);
350
451
  } else {
351
- s.stop(`${success.length} environment variables pushed`);
452
+ s.stop("Credentials pushed to Vercel");
352
453
  }
353
- p2.log.step("Vercel: redeploying with full configuration...");
354
- console.log("");
355
- const redeployOk = execInteractive("vercel deploy --prod --yes", projectDir);
356
- console.log("");
357
- if (redeployOk) {
358
- p2.log.success("Redeployed with full configuration");
454
+ s.start("Redeploying with full configuration...");
455
+ const redeployResult = exec("vercel deploy --prod --yes", projectDir, 3e5);
456
+ if (redeployResult.success) {
457
+ s.stop("Redeployed successfully");
359
458
  } else {
360
- p2.log.warning("Redeploy failed \u2014 env vars will apply on next deploy/push");
459
+ s.stop("Redeploy pending \u2014 will apply on next git push");
361
460
  }
362
461
  return {
363
462
  productionUrl,
@@ -675,7 +675,7 @@ var init_default = defineCommand({
675
675
  })();
676
676
  if (shouldDeploy) {
677
677
  deployAttempted = true;
678
- const { runDeployPipeline } = await import("./deploy-GDDFTPNG.js");
678
+ const { runDeployPipeline } = await import("./deploy-4GLNQM3I.js");
679
679
  deployResult = await runDeployPipeline({
680
680
  projectDir,
681
681
  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-6EDYLQLE.js";
12
+ } from "./chunk-TNUTE5L4.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-6EDYLQLE.js";
4
+ } from "./chunk-TNUTE5L4.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": "0.7.0",
3
+ "version": "0.8.0",
4
4
  "description": "Scaffold and manage Whop-powered apps with whop-kit",
5
5
  "type": "module",
6
6
  "license": "MIT",