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
|
|
11
|
-
import
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
30
|
+
s.stop(`Vercel CLI updated: ${currentVersion} \u2192 ${newVer}`);
|
|
31
31
|
} else {
|
|
32
|
-
|
|
32
|
+
s.stop(`Vercel CLI up to date (v${currentVersion})`);
|
|
33
33
|
}
|
|
34
34
|
} else {
|
|
35
|
-
|
|
35
|
+
s.stop(`Vercel CLI v${currentVersion} (update check failed, continuing)`);
|
|
36
36
|
}
|
|
37
37
|
return true;
|
|
38
38
|
}
|
|
39
|
-
|
|
39
|
+
s.start("Installing Vercel CLI...");
|
|
40
40
|
const result = exec("npm install -g vercel@latest");
|
|
41
41
|
if (result.success) {
|
|
42
|
-
|
|
42
|
+
s.stop("Vercel CLI installed");
|
|
43
43
|
return true;
|
|
44
44
|
}
|
|
45
|
-
|
|
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
|
|
125
|
-
|
|
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
|
-
|
|
128
|
+
s.stop("GitHub CLI installed");
|
|
129
129
|
return true;
|
|
130
130
|
}
|
|
131
|
-
|
|
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
|
|
145
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
162
|
+
s.stop("GitHub repo created");
|
|
163
163
|
}
|
|
164
|
-
|
|
165
|
-
|
|
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
|
-
|
|
171
|
+
s.stop(`Waiting for GitHub to propagate...`);
|
|
172
172
|
await new Promise((r) => setTimeout(r, delay));
|
|
173
|
-
|
|
174
|
-
|
|
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
|
-
|
|
180
|
+
s.stop(`Could not push (try manually: git push -u origin ${branch})`);
|
|
181
181
|
} else {
|
|
182
|
-
|
|
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 (
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
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
|
-
|
|
221
|
-
|
|
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 (
|
|
235
|
-
const
|
|
236
|
-
|
|
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
|
|
240
|
-
|
|
241
|
-
|
|
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 (
|
|
261
|
-
const
|
|
262
|
-
|
|
263
|
-
|
|
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
|
|
266
|
-
|
|
267
|
-
|
|
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
|
|
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 (
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
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 (!
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
384
|
+
p4.log.success(`Signed in${vercelUser ? ` as ${pc4.bold(vercelUser)}` : ""}`);
|
|
373
385
|
await vercelLink(projectDir);
|
|
374
386
|
if (githubRepoUrl) {
|
|
375
|
-
const
|
|
376
|
-
|
|
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
|
-
|
|
391
|
+
s.stop("Connected \u2014 every git push will auto-deploy");
|
|
380
392
|
} else {
|
|
381
|
-
|
|
393
|
+
s.stop("Auto-connect failed (connect manually in Vercel dashboard \u2192 Git)");
|
|
382
394
|
}
|
|
383
395
|
}
|
|
384
396
|
if (databaseUrl) {
|
|
385
|
-
let
|
|
386
|
-
|
|
397
|
+
let s = p4.spinner();
|
|
398
|
+
s.start("Setting DATABASE_URL \u2192 production...");
|
|
387
399
|
vercelEnvSet("DATABASE_URL", databaseUrl, "production", projectDir);
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
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
|
-
|
|
393
|
-
|
|
394
|
-
|
|
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
|
-
|
|
408
|
+
s.stop("DATABASE_URL \u2192 development \u2713");
|
|
397
409
|
}
|
|
398
410
|
productionUrl = await vercelDeploy(projectDir);
|
|
399
411
|
if (!productionUrl) {
|
|
400
|
-
|
|
412
|
+
p4.log.error("Deploy failed. Try: " + pc4.bold(`cd ${projectName} && vercel deploy --prod`));
|
|
401
413
|
}
|
|
402
414
|
}
|
|
403
415
|
if (productionUrl) {
|
|
404
|
-
|
|
405
|
-
const connectWhop = await
|
|
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 (!
|
|
410
|
-
|
|
421
|
+
if (!p4.isCancel(connectWhop) && connectWhop) {
|
|
422
|
+
p4.note(
|
|
411
423
|
[
|
|
412
|
-
|
|
413
|
-
"",
|
|
414
|
-
`${
|
|
415
|
-
`${
|
|
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
|
|
433
|
+
let keyValid = false;
|
|
425
434
|
for (let attempt = 0; attempt < 3; attempt++) {
|
|
426
435
|
if (!apiKey) {
|
|
427
|
-
const result = await
|
|
428
|
-
message: attempt === 0 ? "Paste your Company API key" : "Paste a new Company API key
|
|
429
|
-
placeholder: "
|
|
430
|
-
validate: (v) => !v ? "
|
|
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 (
|
|
441
|
+
if (p4.isCancel(result)) {
|
|
433
442
|
return { productionUrl, githubUrl: githubRepoUrl ?? void 0 };
|
|
434
443
|
}
|
|
435
444
|
apiKey = result;
|
|
436
445
|
}
|
|
437
|
-
|
|
438
|
-
s2.start("Validating
|
|
439
|
-
|
|
440
|
-
if (
|
|
441
|
-
s2.stop(
|
|
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
|
|
453
|
+
s2.stop("API key invalid");
|
|
445
454
|
if (attempt < 2) {
|
|
446
|
-
|
|
447
|
-
|
|
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 (!
|
|
461
|
-
|
|
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
|
-
|
|
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: ${
|
|
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"] =
|
|
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
|
-
|
|
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:
|
|
530
|
+
whopApiKey: appApiKey || void 0,
|
|
508
531
|
webhookSecret: webhook?.secret
|
|
509
532
|
};
|
|
510
533
|
}
|
package/dist/cli-create.js
CHANGED
|
@@ -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-
|
|
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