@whop/cli 0.10.1 → 0.11.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.
- package/README.md +21 -6
- package/dist/index.js +1708 -1105
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
- package/skills/whop-ad-campaigns/SKILL.md +1 -0
- package/skills/whop-verifications/SKILL.md +2 -2
package/dist/index.js
CHANGED
|
@@ -3807,13 +3807,13 @@ function splitCommand(input) {
|
|
|
3807
3807
|
return tokens;
|
|
3808
3808
|
}
|
|
3809
3809
|
function exec(cmd, args) {
|
|
3810
|
-
return new Promise((
|
|
3810
|
+
return new Promise((resolve9, reject) => {
|
|
3811
3811
|
execFile(cmd, args, (error, stdout, stderr) => {
|
|
3812
3812
|
if (error) {
|
|
3813
3813
|
const msg = stderr?.trim() || stdout?.trim() || error.message;
|
|
3814
3814
|
reject(new Error(msg));
|
|
3815
3815
|
} else
|
|
3816
|
-
|
|
3816
|
+
resolve9({ stdout, stderr });
|
|
3817
3817
|
});
|
|
3818
3818
|
});
|
|
3819
3819
|
}
|
|
@@ -6159,7 +6159,7 @@ async function waitForMcpDoctorResponses(chunks, finished) {
|
|
|
6159
6159
|
while (!finished() && (chunks.join("").match(/\n/g)?.length ?? 0) < 2) {
|
|
6160
6160
|
if (Date.now() - started >= 1e3)
|
|
6161
6161
|
return;
|
|
6162
|
-
await new Promise((
|
|
6162
|
+
await new Promise((resolve9) => setTimeout(resolve9, 5));
|
|
6163
6163
|
}
|
|
6164
6164
|
}
|
|
6165
6165
|
function parseMcpDoctorResponses(chunks) {
|
|
@@ -6699,7 +6699,7 @@ var native_spec_default = {
|
|
|
6699
6699
|
termsOfService: "https://whop.com/tos-developer-api/",
|
|
6700
6700
|
title: "Whop API",
|
|
6701
6701
|
version: "1.0.0",
|
|
6702
|
-
"x-api-version-date": "2026-07-
|
|
6702
|
+
"x-api-version-date": "2026-07-18"
|
|
6703
6703
|
},
|
|
6704
6704
|
servers: [
|
|
6705
6705
|
{
|
|
@@ -6758,9 +6758,9 @@ var native_spec_default = {
|
|
|
6758
6758
|
"x-whop-summary": "Convert a balance between currencies."
|
|
6759
6759
|
},
|
|
6760
6760
|
{
|
|
6761
|
-
description: "A Verification represents
|
|
6761
|
+
description: "A Verification represents a legal identity for a person or business. Accounts and users complete verification when Whop needs to confirm who they are before enabling payouts or compliance-sensitive workflows.\n\nUse the Verifications API to start or resume a hosted verification session, check review status, and submit requested details or documents. If `requested_information` contains items, submit answers with [Update Verification](/api-reference/beta/verifications/update-verification).\n",
|
|
6762
6762
|
name: "Verifications",
|
|
6763
|
-
"x-whop-summary": "
|
|
6763
|
+
"x-whop-summary": "Legal identity required before payouts and card issuing."
|
|
6764
6764
|
},
|
|
6765
6765
|
{
|
|
6766
6766
|
description: "A Product is a digital good or service sold on Whop. Products may contain plans for pricing and/or experiences for content delivery.\n\nUse the Products API to create products, list products visible to your credentials, retrieve product details, update product metadata or merchandising fields, and delete products that should no longer be sold.\n",
|
|
@@ -9333,6 +9333,59 @@ var native_spec_default = {
|
|
|
9333
9333
|
]
|
|
9334
9334
|
}
|
|
9335
9335
|
},
|
|
9336
|
+
"/ad_campaigns/{id}/retry_payment": {
|
|
9337
|
+
post: {
|
|
9338
|
+
description: "Retries billing for an ad campaign whose payment previously failed.",
|
|
9339
|
+
operationId: "retry_payment",
|
|
9340
|
+
responses: {
|
|
9341
|
+
"200": {
|
|
9342
|
+
content: {
|
|
9343
|
+
"application/json": {
|
|
9344
|
+
schema: {
|
|
9345
|
+
$ref: "#/components/schemas/AdCampaign"
|
|
9346
|
+
}
|
|
9347
|
+
}
|
|
9348
|
+
},
|
|
9349
|
+
description: "payment retried"
|
|
9350
|
+
},
|
|
9351
|
+
"400": {
|
|
9352
|
+
$ref: "#/components/responses/InvalidParameters",
|
|
9353
|
+
description: "campaign is not in payment_failed status"
|
|
9354
|
+
}
|
|
9355
|
+
},
|
|
9356
|
+
security: [
|
|
9357
|
+
{
|
|
9358
|
+
bearerAuth: [
|
|
9359
|
+
"ad_campaign:update"
|
|
9360
|
+
]
|
|
9361
|
+
}
|
|
9362
|
+
],
|
|
9363
|
+
summary: "Retry a Failed Ad Campaign Payment",
|
|
9364
|
+
tags: [
|
|
9365
|
+
"Ad Campaigns"
|
|
9366
|
+
],
|
|
9367
|
+
"x-codeSamples": [
|
|
9368
|
+
{
|
|
9369
|
+
lang: "JavaScript",
|
|
9370
|
+
source: "import Whop from '@whop/sdk';\n\nconst client = new Whop({\n apiKey: process.env['WHOP_API_KEY'], // This is the default and can be omitted\n});\n\nconst adCampaign = await client.adCampaigns.retryPayment('id');\n\nconsole.log(adCampaign.id);"
|
|
9371
|
+
}
|
|
9372
|
+
],
|
|
9373
|
+
parameters: [
|
|
9374
|
+
{
|
|
9375
|
+
$ref: "#/components/parameters/ApiVersionDate"
|
|
9376
|
+
},
|
|
9377
|
+
{
|
|
9378
|
+
description: "The ad campaign ID.",
|
|
9379
|
+
in: "path",
|
|
9380
|
+
name: "id",
|
|
9381
|
+
required: true,
|
|
9382
|
+
schema: {
|
|
9383
|
+
type: "string"
|
|
9384
|
+
}
|
|
9385
|
+
}
|
|
9386
|
+
]
|
|
9387
|
+
}
|
|
9388
|
+
},
|
|
9336
9389
|
"/ad_campaigns/{id}/unpause": {
|
|
9337
9390
|
post: {
|
|
9338
9391
|
description: "Resumes a paused ad campaign.",
|
|
@@ -14987,7 +15040,7 @@ var native_spec_default = {
|
|
|
14987
15040
|
{
|
|
14988
15041
|
description: "Account ID, prefixed `biz_`.",
|
|
14989
15042
|
in: "query",
|
|
14990
|
-
name: "
|
|
15043
|
+
name: "account_id",
|
|
14991
15044
|
required: true,
|
|
14992
15045
|
schema: {
|
|
14993
15046
|
type: "string"
|
|
@@ -15075,6 +15128,10 @@ var native_spec_default = {
|
|
|
15075
15128
|
data: {
|
|
15076
15129
|
items: {
|
|
15077
15130
|
properties: {
|
|
15131
|
+
account_id: {
|
|
15132
|
+
description: "Account ID, prefixed `biz_`.",
|
|
15133
|
+
type: "string"
|
|
15134
|
+
},
|
|
15078
15135
|
affiliate_code: {
|
|
15079
15136
|
description: "Affiliate code applied at checkout, or `null` when none is set.",
|
|
15080
15137
|
type: [
|
|
@@ -15082,10 +15139,6 @@ var native_spec_default = {
|
|
|
15082
15139
|
"null"
|
|
15083
15140
|
]
|
|
15084
15141
|
},
|
|
15085
|
-
company_id: {
|
|
15086
|
-
description: "Account ID, prefixed `biz_`.",
|
|
15087
|
-
type: "string"
|
|
15088
|
-
},
|
|
15089
15142
|
created_at: {
|
|
15090
15143
|
description: "When the checkout configuration was created, as an ISO 8601 timestamp.",
|
|
15091
15144
|
type: "string"
|
|
@@ -15256,7 +15309,7 @@ var native_spec_default = {
|
|
|
15256
15309
|
"id",
|
|
15257
15310
|
"created_at",
|
|
15258
15311
|
"updated_at",
|
|
15259
|
-
"
|
|
15312
|
+
"account_id",
|
|
15260
15313
|
"mode"
|
|
15261
15314
|
],
|
|
15262
15315
|
type: "object"
|
|
@@ -15305,7 +15358,7 @@ var native_spec_default = {
|
|
|
15305
15358
|
},
|
|
15306
15359
|
"400": {
|
|
15307
15360
|
$ref: "#/components/responses/InvalidParameters",
|
|
15308
|
-
description: "
|
|
15361
|
+
description: "account_id missing"
|
|
15309
15362
|
},
|
|
15310
15363
|
"401": {
|
|
15311
15364
|
$ref: "#/components/responses/Unauthorized",
|
|
@@ -15341,10 +15394,14 @@ var native_spec_default = {
|
|
|
15341
15394
|
"application/json": {
|
|
15342
15395
|
schema: {
|
|
15343
15396
|
example: {
|
|
15344
|
-
|
|
15397
|
+
account_id: "biz_xxxxxxxxxxxxxx",
|
|
15345
15398
|
plan_id: "plan_xxxxxxxxxxxxx"
|
|
15346
15399
|
},
|
|
15347
15400
|
properties: {
|
|
15401
|
+
account_id: {
|
|
15402
|
+
description: "Account ID, prefixed `biz_`.",
|
|
15403
|
+
type: "string"
|
|
15404
|
+
},
|
|
15348
15405
|
affiliate_code: {
|
|
15349
15406
|
description: "Affiliate code to apply to the checkout.",
|
|
15350
15407
|
type: [
|
|
@@ -15352,10 +15409,6 @@ var native_spec_default = {
|
|
|
15352
15409
|
"null"
|
|
15353
15410
|
]
|
|
15354
15411
|
},
|
|
15355
|
-
company_id: {
|
|
15356
|
-
description: "Account ID, prefixed `biz_`.",
|
|
15357
|
-
type: "string"
|
|
15358
|
-
},
|
|
15359
15412
|
currency: {
|
|
15360
15413
|
description: "Currency used for setup-mode payment method availability.",
|
|
15361
15414
|
type: [
|
|
@@ -15408,17 +15461,17 @@ var native_spec_default = {
|
|
|
15408
15461
|
plan: {
|
|
15409
15462
|
description: "Plan attributes used to create or find a plan for this checkout configuration. Mutually exclusive with `plan_id`.",
|
|
15410
15463
|
properties: {
|
|
15411
|
-
|
|
15412
|
-
description: "
|
|
15464
|
+
account_id: {
|
|
15465
|
+
description: "Account ID for the inline plan, prefixed `biz_`. Defaults to the account resolved from the request.",
|
|
15413
15466
|
type: [
|
|
15414
|
-
"
|
|
15467
|
+
"string",
|
|
15415
15468
|
"null"
|
|
15416
15469
|
]
|
|
15417
15470
|
},
|
|
15418
|
-
|
|
15419
|
-
description: "
|
|
15471
|
+
billing_period: {
|
|
15472
|
+
description: "Recurring billing interval in days, such as 30 for monthly or 365 for annual.",
|
|
15420
15473
|
type: [
|
|
15421
|
-
"
|
|
15474
|
+
"integer",
|
|
15422
15475
|
"null"
|
|
15423
15476
|
]
|
|
15424
15477
|
},
|
|
@@ -15600,6 +15653,10 @@ var native_spec_default = {
|
|
|
15600
15653
|
"application/json": {
|
|
15601
15654
|
schema: {
|
|
15602
15655
|
properties: {
|
|
15656
|
+
account_id: {
|
|
15657
|
+
description: "Account ID, prefixed `biz_`.",
|
|
15658
|
+
type: "string"
|
|
15659
|
+
},
|
|
15603
15660
|
affiliate_code: {
|
|
15604
15661
|
description: "Affiliate code applied at checkout, or `null` when none is set.",
|
|
15605
15662
|
type: [
|
|
@@ -15607,10 +15664,6 @@ var native_spec_default = {
|
|
|
15607
15664
|
"null"
|
|
15608
15665
|
]
|
|
15609
15666
|
},
|
|
15610
|
-
company_id: {
|
|
15611
|
-
description: "Account ID, prefixed `biz_`.",
|
|
15612
|
-
type: "string"
|
|
15613
|
-
},
|
|
15614
15667
|
created_at: {
|
|
15615
15668
|
description: "When the checkout configuration was created, as an ISO 8601 timestamp.",
|
|
15616
15669
|
type: "string"
|
|
@@ -15781,7 +15834,7 @@ var native_spec_default = {
|
|
|
15781
15834
|
"id",
|
|
15782
15835
|
"created_at",
|
|
15783
15836
|
"updated_at",
|
|
15784
|
-
"
|
|
15837
|
+
"account_id",
|
|
15785
15838
|
"mode"
|
|
15786
15839
|
],
|
|
15787
15840
|
type: "object"
|
|
@@ -15868,6 +15921,10 @@ var native_spec_default = {
|
|
|
15868
15921
|
"application/json": {
|
|
15869
15922
|
schema: {
|
|
15870
15923
|
properties: {
|
|
15924
|
+
account_id: {
|
|
15925
|
+
description: "Account ID, prefixed `biz_`.",
|
|
15926
|
+
type: "string"
|
|
15927
|
+
},
|
|
15871
15928
|
affiliate_code: {
|
|
15872
15929
|
description: "Affiliate code applied at checkout, or `null` when none is set.",
|
|
15873
15930
|
type: [
|
|
@@ -15875,10 +15932,6 @@ var native_spec_default = {
|
|
|
15875
15932
|
"null"
|
|
15876
15933
|
]
|
|
15877
15934
|
},
|
|
15878
|
-
company_id: {
|
|
15879
|
-
description: "Account ID, prefixed `biz_`.",
|
|
15880
|
-
type: "string"
|
|
15881
|
-
},
|
|
15882
15935
|
created_at: {
|
|
15883
15936
|
description: "When the checkout configuration was created, as an ISO 8601 timestamp.",
|
|
15884
15937
|
type: "string"
|
|
@@ -16049,7 +16102,7 @@ var native_spec_default = {
|
|
|
16049
16102
|
"id",
|
|
16050
16103
|
"created_at",
|
|
16051
16104
|
"updated_at",
|
|
16052
|
-
"
|
|
16105
|
+
"account_id",
|
|
16053
16106
|
"mode"
|
|
16054
16107
|
],
|
|
16055
16108
|
type: "object"
|
|
@@ -18496,6 +18549,49 @@ var native_spec_default = {
|
|
|
18496
18549
|
"null"
|
|
18497
18550
|
]
|
|
18498
18551
|
},
|
|
18552
|
+
second_tier_partner: {
|
|
18553
|
+
description: "The second-tier partner who earns on this business (referred the first-tier partner). Null if there is no active second-tier partner.",
|
|
18554
|
+
properties: {
|
|
18555
|
+
id: {
|
|
18556
|
+
description: "User ID, prefixed `user_`.",
|
|
18557
|
+
type: "string"
|
|
18558
|
+
},
|
|
18559
|
+
name: {
|
|
18560
|
+
description: "The user's display name.",
|
|
18561
|
+
type: [
|
|
18562
|
+
"string",
|
|
18563
|
+
"null"
|
|
18564
|
+
]
|
|
18565
|
+
},
|
|
18566
|
+
profile_picture: {
|
|
18567
|
+
description: "The user's profile picture.",
|
|
18568
|
+
properties: {
|
|
18569
|
+
url: {
|
|
18570
|
+
description: "The user's profile picture URL.",
|
|
18571
|
+
type: "string"
|
|
18572
|
+
}
|
|
18573
|
+
},
|
|
18574
|
+
required: [
|
|
18575
|
+
"url"
|
|
18576
|
+
],
|
|
18577
|
+
type: "object"
|
|
18578
|
+
},
|
|
18579
|
+
username: {
|
|
18580
|
+
description: "The user's unique username.",
|
|
18581
|
+
type: "string"
|
|
18582
|
+
}
|
|
18583
|
+
},
|
|
18584
|
+
required: [
|
|
18585
|
+
"id",
|
|
18586
|
+
"username",
|
|
18587
|
+
"name",
|
|
18588
|
+
"profile_picture"
|
|
18589
|
+
],
|
|
18590
|
+
type: [
|
|
18591
|
+
"object",
|
|
18592
|
+
"null"
|
|
18593
|
+
]
|
|
18594
|
+
},
|
|
18499
18595
|
status: {
|
|
18500
18596
|
description: "Current referral status.",
|
|
18501
18597
|
enum: [
|
|
@@ -18534,6 +18630,7 @@ var native_spec_default = {
|
|
|
18534
18630
|
"account",
|
|
18535
18631
|
"owner",
|
|
18536
18632
|
"first_tier_partner",
|
|
18633
|
+
"second_tier_partner",
|
|
18537
18634
|
"my_partner_tier",
|
|
18538
18635
|
"status",
|
|
18539
18636
|
"payout_percentage",
|
|
@@ -18842,6 +18939,49 @@ var native_spec_default = {
|
|
|
18842
18939
|
"null"
|
|
18843
18940
|
]
|
|
18844
18941
|
},
|
|
18942
|
+
second_tier_partner: {
|
|
18943
|
+
description: "The second-tier partner who earns on this business (referred the first-tier partner). Null if there is no active second-tier partner.",
|
|
18944
|
+
properties: {
|
|
18945
|
+
id: {
|
|
18946
|
+
description: "User ID, prefixed `user_`.",
|
|
18947
|
+
type: "string"
|
|
18948
|
+
},
|
|
18949
|
+
name: {
|
|
18950
|
+
description: "The user's display name.",
|
|
18951
|
+
type: [
|
|
18952
|
+
"string",
|
|
18953
|
+
"null"
|
|
18954
|
+
]
|
|
18955
|
+
},
|
|
18956
|
+
profile_picture: {
|
|
18957
|
+
description: "The user's profile picture.",
|
|
18958
|
+
properties: {
|
|
18959
|
+
url: {
|
|
18960
|
+
description: "The user's profile picture URL.",
|
|
18961
|
+
type: "string"
|
|
18962
|
+
}
|
|
18963
|
+
},
|
|
18964
|
+
required: [
|
|
18965
|
+
"url"
|
|
18966
|
+
],
|
|
18967
|
+
type: "object"
|
|
18968
|
+
},
|
|
18969
|
+
username: {
|
|
18970
|
+
description: "The user's unique username.",
|
|
18971
|
+
type: "string"
|
|
18972
|
+
}
|
|
18973
|
+
},
|
|
18974
|
+
required: [
|
|
18975
|
+
"id",
|
|
18976
|
+
"username",
|
|
18977
|
+
"name",
|
|
18978
|
+
"profile_picture"
|
|
18979
|
+
],
|
|
18980
|
+
type: [
|
|
18981
|
+
"object",
|
|
18982
|
+
"null"
|
|
18983
|
+
]
|
|
18984
|
+
},
|
|
18845
18985
|
status: {
|
|
18846
18986
|
description: "Current referral status.",
|
|
18847
18987
|
enum: [
|
|
@@ -18879,6 +19019,7 @@ var native_spec_default = {
|
|
|
18879
19019
|
"account",
|
|
18880
19020
|
"owner",
|
|
18881
19021
|
"first_tier_partner",
|
|
19022
|
+
"second_tier_partner",
|
|
18882
19023
|
"my_partner_tier",
|
|
18883
19024
|
"status",
|
|
18884
19025
|
"payout_percentage",
|
|
@@ -19723,7 +19864,7 @@ var native_spec_default = {
|
|
|
19723
19864
|
"x-codeSamples": [
|
|
19724
19865
|
{
|
|
19725
19866
|
lang: "JavaScript",
|
|
19726
|
-
source: "import Whop from '@whop/sdk';\n\nconst client = new Whop(
|
|
19867
|
+
source: "import Whop from '@whop/sdk';\n\nconst client = new Whop();\n\nconst response = await client.partners.leaderboard();\n\nconsole.log(response.leaders);"
|
|
19727
19868
|
}
|
|
19728
19869
|
]
|
|
19729
19870
|
}
|
|
@@ -23085,9 +23226,9 @@ var native_spec_default = {
|
|
|
23085
23226
|
$ref: "#/components/parameters/ApiVersionDate"
|
|
23086
23227
|
},
|
|
23087
23228
|
{
|
|
23088
|
-
description: "The unique identifier of the
|
|
23229
|
+
description: "The unique identifier of the account to list products for.",
|
|
23089
23230
|
in: "query",
|
|
23090
|
-
name: "
|
|
23231
|
+
name: "account_id",
|
|
23091
23232
|
required: true,
|
|
23092
23233
|
schema: {
|
|
23093
23234
|
type: "string"
|
|
@@ -23230,7 +23371,7 @@ var native_spec_default = {
|
|
|
23230
23371
|
},
|
|
23231
23372
|
"400": {
|
|
23232
23373
|
$ref: "#/components/responses/InvalidParameters",
|
|
23233
|
-
description: "missing
|
|
23374
|
+
description: "missing account_id"
|
|
23234
23375
|
},
|
|
23235
23376
|
"401": {
|
|
23236
23377
|
$ref: "#/components/responses/Unauthorized",
|
|
@@ -23268,6 +23409,10 @@ var native_spec_default = {
|
|
|
23268
23409
|
"application/json": {
|
|
23269
23410
|
schema: {
|
|
23270
23411
|
properties: {
|
|
23412
|
+
account_id: {
|
|
23413
|
+
description: "The unique identifier of the account to create this product for.",
|
|
23414
|
+
type: "string"
|
|
23415
|
+
},
|
|
23271
23416
|
collect_shipping_address: {
|
|
23272
23417
|
description: "Whether to collect a shipping address at checkout.",
|
|
23273
23418
|
type: [
|
|
@@ -23275,10 +23420,6 @@ var native_spec_default = {
|
|
|
23275
23420
|
"null"
|
|
23276
23421
|
]
|
|
23277
23422
|
},
|
|
23278
|
-
company_id: {
|
|
23279
|
-
description: "The unique identifier of the company to create this product for.",
|
|
23280
|
-
type: "string"
|
|
23281
|
-
},
|
|
23282
23423
|
custom_cta: {
|
|
23283
23424
|
description: "The call-to-action button label.",
|
|
23284
23425
|
type: [
|
|
@@ -24380,6 +24521,7 @@ var native_spec_default = {
|
|
|
24380
24521
|
required: false,
|
|
24381
24522
|
schema: {
|
|
24382
24523
|
enum: [
|
|
24524
|
+
"minute",
|
|
24383
24525
|
"five_minutes",
|
|
24384
24526
|
"thirty_minutes",
|
|
24385
24527
|
"hour",
|
|
@@ -24455,7 +24597,7 @@ var native_spec_default = {
|
|
|
24455
24597
|
}
|
|
24456
24598
|
},
|
|
24457
24599
|
{
|
|
24458
|
-
description: "Filter to a single GMV source, for example payments. Pair with breakdown_by=source to split by source. Available on metrics that list source.",
|
|
24600
|
+
description: "Filter to a single GMV source, for example payments \u2014 or, on the traffic metrics, a visit source (whop_ads, direct, or a utm_source value). Pair with breakdown_by=source to split by source. Available on metrics that list source.",
|
|
24459
24601
|
in: "query",
|
|
24460
24602
|
name: "source",
|
|
24461
24603
|
required: false,
|
|
@@ -24463,6 +24605,74 @@ var native_spec_default = {
|
|
|
24463
24605
|
type: "string"
|
|
24464
24606
|
}
|
|
24465
24607
|
},
|
|
24608
|
+
{
|
|
24609
|
+
description: "Filter traffic metrics to one website hostname, for example shop.example.com. Pair with breakdown_by=hostname to split by website.",
|
|
24610
|
+
in: "query",
|
|
24611
|
+
name: "hostname",
|
|
24612
|
+
required: false,
|
|
24613
|
+
schema: {
|
|
24614
|
+
type: "string"
|
|
24615
|
+
}
|
|
24616
|
+
},
|
|
24617
|
+
{
|
|
24618
|
+
description: "Filter traffic metrics to one page \u2014 a hostname plus normalized path, for example shop.example.com/pricing. Pair with breakdown_by=page to split by page.",
|
|
24619
|
+
in: "query",
|
|
24620
|
+
name: "page",
|
|
24621
|
+
required: false,
|
|
24622
|
+
schema: {
|
|
24623
|
+
type: "string"
|
|
24624
|
+
}
|
|
24625
|
+
},
|
|
24626
|
+
{
|
|
24627
|
+
description: "Filter traffic metrics to one device type: desktop, mobile, tablet, or unknown. Pair with breakdown_by=device_type to split by device.",
|
|
24628
|
+
in: "query",
|
|
24629
|
+
name: "device_type",
|
|
24630
|
+
required: false,
|
|
24631
|
+
schema: {
|
|
24632
|
+
type: "string"
|
|
24633
|
+
}
|
|
24634
|
+
},
|
|
24635
|
+
{
|
|
24636
|
+
description: "Filter traffic metrics to one visitor country (uppercase ISO 3166-1 alpha-2, for example US). Pair with breakdown_by=country_code to split by country.",
|
|
24637
|
+
in: "query",
|
|
24638
|
+
name: "country_code",
|
|
24639
|
+
required: false,
|
|
24640
|
+
schema: {
|
|
24641
|
+
type: "string"
|
|
24642
|
+
}
|
|
24643
|
+
},
|
|
24644
|
+
{
|
|
24645
|
+
description: "Filter the events metric to one tracked event name, for example pixel.page or pixel.custom. Pair with breakdown_by=event_name to split by event.",
|
|
24646
|
+
in: "query",
|
|
24647
|
+
name: "event_name",
|
|
24648
|
+
required: false,
|
|
24649
|
+
schema: {
|
|
24650
|
+
type: "string"
|
|
24651
|
+
}
|
|
24652
|
+
},
|
|
24653
|
+
{
|
|
24654
|
+
description: "Filter the events metric to a canonical group of events: page_view (pixel page views plus whop.com store views), checkout_start (hosted and embedded checkout views), or other. Pair with breakdown_by=event_type to split by group.",
|
|
24655
|
+
in: "query",
|
|
24656
|
+
name: "event_type",
|
|
24657
|
+
required: false,
|
|
24658
|
+
schema: {
|
|
24659
|
+
enum: [
|
|
24660
|
+
"page_view",
|
|
24661
|
+
"checkout_start",
|
|
24662
|
+
"other"
|
|
24663
|
+
],
|
|
24664
|
+
type: "string"
|
|
24665
|
+
}
|
|
24666
|
+
},
|
|
24667
|
+
{
|
|
24668
|
+
description: "Filter the events metric to one merchant-defined custom event name. Only valid alongside event_name=pixel.custom. Pair with breakdown_by=custom_name to split custom events by name.",
|
|
24669
|
+
in: "query",
|
|
24670
|
+
name: "custom_name",
|
|
24671
|
+
required: false,
|
|
24672
|
+
schema: {
|
|
24673
|
+
type: "string"
|
|
24674
|
+
}
|
|
24675
|
+
},
|
|
24466
24676
|
{
|
|
24467
24677
|
description: "Filter to a single wallet-balance segment, for example available. Pair with breakdown_by=segment to split the balance. Available on metrics that list segment.",
|
|
24468
24678
|
in: "query",
|
|
@@ -28798,7 +29008,7 @@ var native_spec_default = {
|
|
|
28798
29008
|
name: "Api-Version-Date",
|
|
28799
29009
|
required: false,
|
|
28800
29010
|
schema: {
|
|
28801
|
-
example: "2026-07-
|
|
29011
|
+
example: "2026-07-18",
|
|
28802
29012
|
type: "string"
|
|
28803
29013
|
}
|
|
28804
29014
|
}
|
|
@@ -35572,7 +35782,7 @@ var apps_spec_default = {
|
|
|
35572
35782
|
termsOfService: "https://whop.com/tos-developer-api/",
|
|
35573
35783
|
title: "Whop API",
|
|
35574
35784
|
version: "1.0.0",
|
|
35575
|
-
"x-api-version-date": "2026-07-
|
|
35785
|
+
"x-api-version-date": "2026-07-18"
|
|
35576
35786
|
},
|
|
35577
35787
|
paths: {
|
|
35578
35788
|
"/app_builds": {
|
|
@@ -36107,482 +36317,24 @@ var apps_spec_default = {
|
|
|
36107
36317
|
"App builds"
|
|
36108
36318
|
],
|
|
36109
36319
|
"x-group-title": "Developer"
|
|
36110
|
-
}
|
|
36111
|
-
|
|
36112
|
-
|
|
36113
|
-
|
|
36114
|
-
|
|
36115
|
-
|
|
36116
|
-
|
|
36117
|
-
|
|
36118
|
-
|
|
36119
|
-
|
|
36120
|
-
|
|
36121
|
-
|
|
36122
|
-
|
|
36123
|
-
|
|
36124
|
-
|
|
36125
|
-
"string",
|
|
36126
|
-
"null"
|
|
36127
|
-
]
|
|
36128
|
-
},
|
|
36129
|
-
app_id: {
|
|
36130
|
-
description: "The unique identifier of the app to create the build for. Defaults to the app associated with the current API key.",
|
|
36131
|
-
example: "app_xxxxxxxxxxxxxx",
|
|
36132
|
-
type: [
|
|
36133
|
-
"string",
|
|
36134
|
-
"null"
|
|
36135
|
-
]
|
|
36136
|
-
},
|
|
36137
|
-
attachment: {
|
|
36138
|
-
description: "The build file to upload. For iOS and Android, this should be a .zip archive containing a main_js_bundle.hbc file and an optional assets folder. For web, this should be a JavaScript file or a .zip archive of the hosted site.",
|
|
36139
|
-
properties: {
|
|
36140
|
-
id: {
|
|
36141
|
-
description: "The ID of an existing file object.",
|
|
36142
|
-
type: "string"
|
|
36143
|
-
}
|
|
36144
|
-
},
|
|
36145
|
-
required: [
|
|
36146
|
-
"id"
|
|
36147
|
-
],
|
|
36148
|
-
title: "FileInputWithId",
|
|
36149
|
-
type: "object"
|
|
36150
|
-
},
|
|
36151
|
-
checksum: {
|
|
36152
|
-
description: "A client-generated checksum of the build file, used to verify file integrity when unpacked on a device.",
|
|
36153
|
-
type: "string"
|
|
36154
|
-
},
|
|
36155
|
-
platform: {
|
|
36156
|
-
$ref: "#/components/schemas/AppBuildPlatforms",
|
|
36157
|
-
description: "The target platform for the build. Accepted values: ios, android, web."
|
|
36158
|
-
},
|
|
36159
|
-
source_attachment: {
|
|
36160
|
-
description: "An optional compressed archive (.zip or .gz) of the source code that produced this build, stored alongside the build so it can be downloaded later.",
|
|
36161
|
-
properties: {
|
|
36162
|
-
id: {
|
|
36163
|
-
description: "The ID of an existing file object.",
|
|
36164
|
-
type: "string"
|
|
36165
|
-
}
|
|
36166
|
-
},
|
|
36167
|
-
required: [
|
|
36168
|
-
"id"
|
|
36169
|
-
],
|
|
36170
|
-
title: "FileInputWithId",
|
|
36171
|
-
type: [
|
|
36172
|
-
"object",
|
|
36173
|
-
"null"
|
|
36174
|
-
]
|
|
36175
|
-
},
|
|
36176
|
-
supported_app_view_types: {
|
|
36177
|
-
description: "The view types this build supports. A build can support multiple view types but should only list the ones its code implements.",
|
|
36178
|
-
items: {
|
|
36179
|
-
$ref: "#/components/schemas/AppViewTypes"
|
|
36180
|
-
},
|
|
36181
|
-
type: [
|
|
36182
|
-
"array",
|
|
36183
|
-
"null"
|
|
36184
|
-
]
|
|
36185
|
-
}
|
|
36186
|
-
},
|
|
36187
|
-
required: [
|
|
36188
|
-
"attachment",
|
|
36189
|
-
"checksum",
|
|
36190
|
-
"platform"
|
|
36191
|
-
],
|
|
36192
|
-
type: "object"
|
|
36193
|
-
}
|
|
36320
|
+
}
|
|
36321
|
+
},
|
|
36322
|
+
"/app_builds/{id}": {
|
|
36323
|
+
get: {
|
|
36324
|
+
description: "Retrieves the details of an existing app build.\n\nRequired permissions:\n - `developer:manage_builds`",
|
|
36325
|
+
operationId: "get",
|
|
36326
|
+
parameters: [
|
|
36327
|
+
{
|
|
36328
|
+
description: "The unique identifier of the app build to retrieve.",
|
|
36329
|
+
in: "path",
|
|
36330
|
+
name: "id",
|
|
36331
|
+
required: true,
|
|
36332
|
+
schema: {
|
|
36333
|
+
example: "apbu_xxxxxxxxxxxxx",
|
|
36334
|
+
type: "string"
|
|
36194
36335
|
}
|
|
36195
|
-
}
|
|
36196
|
-
|
|
36197
|
-
},
|
|
36198
|
-
responses: {
|
|
36199
|
-
"200": {
|
|
36200
|
-
content: {
|
|
36201
|
-
"application/json": {
|
|
36202
|
-
schema: {
|
|
36203
|
-
$ref: "#/components/schemas/AppBuild"
|
|
36204
|
-
}
|
|
36205
|
-
}
|
|
36206
|
-
},
|
|
36207
|
-
description: "A successful response"
|
|
36208
|
-
},
|
|
36209
|
-
"400": {
|
|
36210
|
-
content: {
|
|
36211
|
-
"application/json": {
|
|
36212
|
-
example: {
|
|
36213
|
-
error: {
|
|
36214
|
-
code: "parameter_missing",
|
|
36215
|
-
message: "Missing required parameter: amount.",
|
|
36216
|
-
param: "amount",
|
|
36217
|
-
type: "invalid_request_error"
|
|
36218
|
-
}
|
|
36219
|
-
},
|
|
36220
|
-
schema: {
|
|
36221
|
-
properties: {
|
|
36222
|
-
error: {
|
|
36223
|
-
properties: {
|
|
36224
|
-
code: {
|
|
36225
|
-
description: "A short string indicating the specific error code, e.g. 'parameter_missing', 'parameter_invalid', 'invalid_json'",
|
|
36226
|
-
type: [
|
|
36227
|
-
"string",
|
|
36228
|
-
"null"
|
|
36229
|
-
]
|
|
36230
|
-
},
|
|
36231
|
-
message: {
|
|
36232
|
-
type: "string"
|
|
36233
|
-
},
|
|
36234
|
-
param: {
|
|
36235
|
-
description: "The parameter that caused the error, if applicable",
|
|
36236
|
-
type: [
|
|
36237
|
-
"string",
|
|
36238
|
-
"null"
|
|
36239
|
-
]
|
|
36240
|
-
},
|
|
36241
|
-
type: {
|
|
36242
|
-
type: "string"
|
|
36243
|
-
}
|
|
36244
|
-
},
|
|
36245
|
-
required: [
|
|
36246
|
-
"type",
|
|
36247
|
-
"message"
|
|
36248
|
-
],
|
|
36249
|
-
type: "object"
|
|
36250
|
-
}
|
|
36251
|
-
},
|
|
36252
|
-
required: [
|
|
36253
|
-
"error"
|
|
36254
|
-
],
|
|
36255
|
-
type: "object"
|
|
36256
|
-
}
|
|
36257
|
-
}
|
|
36258
|
-
},
|
|
36259
|
-
description: "Bad request"
|
|
36260
|
-
},
|
|
36261
|
-
"401": {
|
|
36262
|
-
content: {
|
|
36263
|
-
"application/json": {
|
|
36264
|
-
example: {
|
|
36265
|
-
error: {
|
|
36266
|
-
message: "Invalid or missing API key",
|
|
36267
|
-
type: "unauthorized"
|
|
36268
|
-
}
|
|
36269
|
-
},
|
|
36270
|
-
schema: {
|
|
36271
|
-
properties: {
|
|
36272
|
-
error: {
|
|
36273
|
-
properties: {
|
|
36274
|
-
code: {
|
|
36275
|
-
description: "A short string indicating the specific error code, e.g. 'parameter_missing', 'parameter_invalid', 'invalid_json'",
|
|
36276
|
-
type: [
|
|
36277
|
-
"string",
|
|
36278
|
-
"null"
|
|
36279
|
-
]
|
|
36280
|
-
},
|
|
36281
|
-
message: {
|
|
36282
|
-
type: "string"
|
|
36283
|
-
},
|
|
36284
|
-
param: {
|
|
36285
|
-
description: "The parameter that caused the error, if applicable",
|
|
36286
|
-
type: [
|
|
36287
|
-
"string",
|
|
36288
|
-
"null"
|
|
36289
|
-
]
|
|
36290
|
-
},
|
|
36291
|
-
type: {
|
|
36292
|
-
type: "string"
|
|
36293
|
-
}
|
|
36294
|
-
},
|
|
36295
|
-
required: [
|
|
36296
|
-
"type",
|
|
36297
|
-
"message"
|
|
36298
|
-
],
|
|
36299
|
-
type: "object"
|
|
36300
|
-
}
|
|
36301
|
-
},
|
|
36302
|
-
required: [
|
|
36303
|
-
"error"
|
|
36304
|
-
],
|
|
36305
|
-
type: "object"
|
|
36306
|
-
}
|
|
36307
|
-
}
|
|
36308
|
-
},
|
|
36309
|
-
description: "Unauthorized"
|
|
36310
|
-
},
|
|
36311
|
-
"403": {
|
|
36312
|
-
content: {
|
|
36313
|
-
"application/json": {
|
|
36314
|
-
example: {
|
|
36315
|
-
error: {
|
|
36316
|
-
message: "You do not have permission to access this resource",
|
|
36317
|
-
type: "forbidden"
|
|
36318
|
-
}
|
|
36319
|
-
},
|
|
36320
|
-
schema: {
|
|
36321
|
-
properties: {
|
|
36322
|
-
error: {
|
|
36323
|
-
properties: {
|
|
36324
|
-
code: {
|
|
36325
|
-
description: "A short string indicating the specific error code, e.g. 'parameter_missing', 'parameter_invalid', 'invalid_json'",
|
|
36326
|
-
type: [
|
|
36327
|
-
"string",
|
|
36328
|
-
"null"
|
|
36329
|
-
]
|
|
36330
|
-
},
|
|
36331
|
-
message: {
|
|
36332
|
-
type: "string"
|
|
36333
|
-
},
|
|
36334
|
-
param: {
|
|
36335
|
-
description: "The parameter that caused the error, if applicable",
|
|
36336
|
-
type: [
|
|
36337
|
-
"string",
|
|
36338
|
-
"null"
|
|
36339
|
-
]
|
|
36340
|
-
},
|
|
36341
|
-
type: {
|
|
36342
|
-
type: "string"
|
|
36343
|
-
}
|
|
36344
|
-
},
|
|
36345
|
-
required: [
|
|
36346
|
-
"type",
|
|
36347
|
-
"message"
|
|
36348
|
-
],
|
|
36349
|
-
type: "object"
|
|
36350
|
-
}
|
|
36351
|
-
},
|
|
36352
|
-
required: [
|
|
36353
|
-
"error"
|
|
36354
|
-
],
|
|
36355
|
-
type: "object"
|
|
36356
|
-
}
|
|
36357
|
-
}
|
|
36358
|
-
},
|
|
36359
|
-
description: "Forbidden"
|
|
36360
|
-
},
|
|
36361
|
-
"404": {
|
|
36362
|
-
content: {
|
|
36363
|
-
"application/json": {
|
|
36364
|
-
example: {
|
|
36365
|
-
error: {
|
|
36366
|
-
message: "Resource not found",
|
|
36367
|
-
type: "not_found"
|
|
36368
|
-
}
|
|
36369
|
-
},
|
|
36370
|
-
schema: {
|
|
36371
|
-
properties: {
|
|
36372
|
-
error: {
|
|
36373
|
-
properties: {
|
|
36374
|
-
code: {
|
|
36375
|
-
description: "A short string indicating the specific error code, e.g. 'parameter_missing', 'parameter_invalid', 'invalid_json'",
|
|
36376
|
-
type: [
|
|
36377
|
-
"string",
|
|
36378
|
-
"null"
|
|
36379
|
-
]
|
|
36380
|
-
},
|
|
36381
|
-
message: {
|
|
36382
|
-
type: "string"
|
|
36383
|
-
},
|
|
36384
|
-
param: {
|
|
36385
|
-
description: "The parameter that caused the error, if applicable",
|
|
36386
|
-
type: [
|
|
36387
|
-
"string",
|
|
36388
|
-
"null"
|
|
36389
|
-
]
|
|
36390
|
-
},
|
|
36391
|
-
type: {
|
|
36392
|
-
type: "string"
|
|
36393
|
-
}
|
|
36394
|
-
},
|
|
36395
|
-
required: [
|
|
36396
|
-
"type",
|
|
36397
|
-
"message"
|
|
36398
|
-
],
|
|
36399
|
-
type: "object"
|
|
36400
|
-
}
|
|
36401
|
-
},
|
|
36402
|
-
required: [
|
|
36403
|
-
"error"
|
|
36404
|
-
],
|
|
36405
|
-
type: "object"
|
|
36406
|
-
}
|
|
36407
|
-
}
|
|
36408
|
-
},
|
|
36409
|
-
description: "Not found"
|
|
36410
|
-
},
|
|
36411
|
-
"422": {
|
|
36412
|
-
content: {
|
|
36413
|
-
"application/json": {
|
|
36414
|
-
example: {
|
|
36415
|
-
error: null
|
|
36416
|
-
},
|
|
36417
|
-
schema: {
|
|
36418
|
-
properties: {
|
|
36419
|
-
error: {
|
|
36420
|
-
properties: {
|
|
36421
|
-
code: {
|
|
36422
|
-
description: "A short string indicating the specific error code, e.g. 'parameter_missing', 'parameter_invalid', 'invalid_json'",
|
|
36423
|
-
type: [
|
|
36424
|
-
"string",
|
|
36425
|
-
"null"
|
|
36426
|
-
]
|
|
36427
|
-
},
|
|
36428
|
-
message: {
|
|
36429
|
-
type: "string"
|
|
36430
|
-
},
|
|
36431
|
-
param: {
|
|
36432
|
-
description: "The parameter that caused the error, if applicable",
|
|
36433
|
-
type: [
|
|
36434
|
-
"string",
|
|
36435
|
-
"null"
|
|
36436
|
-
]
|
|
36437
|
-
},
|
|
36438
|
-
type: {
|
|
36439
|
-
type: "string"
|
|
36440
|
-
}
|
|
36441
|
-
},
|
|
36442
|
-
required: [
|
|
36443
|
-
"type",
|
|
36444
|
-
"message"
|
|
36445
|
-
],
|
|
36446
|
-
type: "object"
|
|
36447
|
-
}
|
|
36448
|
-
},
|
|
36449
|
-
required: [
|
|
36450
|
-
"error"
|
|
36451
|
-
],
|
|
36452
|
-
type: "object"
|
|
36453
|
-
}
|
|
36454
|
-
}
|
|
36455
|
-
},
|
|
36456
|
-
description: "Verification required"
|
|
36457
|
-
},
|
|
36458
|
-
"429": {
|
|
36459
|
-
content: {
|
|
36460
|
-
"application/json": {
|
|
36461
|
-
example: {
|
|
36462
|
-
error: null
|
|
36463
|
-
},
|
|
36464
|
-
schema: {
|
|
36465
|
-
properties: {
|
|
36466
|
-
error: {
|
|
36467
|
-
properties: {
|
|
36468
|
-
code: {
|
|
36469
|
-
description: "A short string indicating the specific error code, e.g. 'parameter_missing', 'parameter_invalid', 'invalid_json'",
|
|
36470
|
-
type: [
|
|
36471
|
-
"string",
|
|
36472
|
-
"null"
|
|
36473
|
-
]
|
|
36474
|
-
},
|
|
36475
|
-
message: {
|
|
36476
|
-
type: "string"
|
|
36477
|
-
},
|
|
36478
|
-
param: {
|
|
36479
|
-
description: "The parameter that caused the error, if applicable",
|
|
36480
|
-
type: [
|
|
36481
|
-
"string",
|
|
36482
|
-
"null"
|
|
36483
|
-
]
|
|
36484
|
-
},
|
|
36485
|
-
type: {
|
|
36486
|
-
type: "string"
|
|
36487
|
-
}
|
|
36488
|
-
},
|
|
36489
|
-
required: [
|
|
36490
|
-
"type",
|
|
36491
|
-
"message"
|
|
36492
|
-
],
|
|
36493
|
-
type: "object"
|
|
36494
|
-
}
|
|
36495
|
-
},
|
|
36496
|
-
required: [
|
|
36497
|
-
"error"
|
|
36498
|
-
],
|
|
36499
|
-
type: "object"
|
|
36500
|
-
}
|
|
36501
|
-
}
|
|
36502
|
-
},
|
|
36503
|
-
description: "Too many requests"
|
|
36504
|
-
},
|
|
36505
|
-
"500": {
|
|
36506
|
-
content: {
|
|
36507
|
-
"application/json": {
|
|
36508
|
-
example: {
|
|
36509
|
-
error: {
|
|
36510
|
-
message: "An unexpected error occurred",
|
|
36511
|
-
type: "internal_server_error"
|
|
36512
|
-
}
|
|
36513
|
-
},
|
|
36514
|
-
schema: {
|
|
36515
|
-
properties: {
|
|
36516
|
-
error: {
|
|
36517
|
-
properties: {
|
|
36518
|
-
code: {
|
|
36519
|
-
description: "A short string indicating the specific error code, e.g. 'parameter_missing', 'parameter_invalid', 'invalid_json'",
|
|
36520
|
-
type: [
|
|
36521
|
-
"string",
|
|
36522
|
-
"null"
|
|
36523
|
-
]
|
|
36524
|
-
},
|
|
36525
|
-
message: {
|
|
36526
|
-
type: "string"
|
|
36527
|
-
},
|
|
36528
|
-
param: {
|
|
36529
|
-
description: "The parameter that caused the error, if applicable",
|
|
36530
|
-
type: [
|
|
36531
|
-
"string",
|
|
36532
|
-
"null"
|
|
36533
|
-
]
|
|
36534
|
-
},
|
|
36535
|
-
type: {
|
|
36536
|
-
type: "string"
|
|
36537
|
-
}
|
|
36538
|
-
},
|
|
36539
|
-
required: [
|
|
36540
|
-
"type",
|
|
36541
|
-
"message"
|
|
36542
|
-
],
|
|
36543
|
-
type: "object"
|
|
36544
|
-
}
|
|
36545
|
-
},
|
|
36546
|
-
required: [
|
|
36547
|
-
"error"
|
|
36548
|
-
],
|
|
36549
|
-
type: "object"
|
|
36550
|
-
}
|
|
36551
|
-
}
|
|
36552
|
-
},
|
|
36553
|
-
description: "Internal server error"
|
|
36554
|
-
}
|
|
36555
|
-
},
|
|
36556
|
-
security: [
|
|
36557
|
-
{
|
|
36558
|
-
bearerAuth: [
|
|
36559
|
-
"developer:manage_builds"
|
|
36560
|
-
]
|
|
36561
|
-
}
|
|
36562
|
-
],
|
|
36563
|
-
summary: "Create app build",
|
|
36564
|
-
tags: [
|
|
36565
|
-
"App builds"
|
|
36566
|
-
],
|
|
36567
|
-
"x-group-title": "Developer"
|
|
36568
|
-
}
|
|
36569
|
-
},
|
|
36570
|
-
"/app_builds/{id}": {
|
|
36571
|
-
get: {
|
|
36572
|
-
description: "Retrieves the details of an existing app build.\n\nRequired permissions:\n - `developer:manage_builds`",
|
|
36573
|
-
operationId: "get",
|
|
36574
|
-
parameters: [
|
|
36575
|
-
{
|
|
36576
|
-
description: "The unique identifier of the app build to retrieve.",
|
|
36577
|
-
in: "path",
|
|
36578
|
-
name: "id",
|
|
36579
|
-
required: true,
|
|
36580
|
-
schema: {
|
|
36581
|
-
example: "apbu_xxxxxxxxxxxxx",
|
|
36582
|
-
type: "string"
|
|
36583
|
-
}
|
|
36584
|
-
}
|
|
36585
|
-
],
|
|
36336
|
+
}
|
|
36337
|
+
],
|
|
36586
36338
|
responses: {
|
|
36587
36339
|
"200": {
|
|
36588
36340
|
content: {
|
|
@@ -39293,6 +39045,261 @@ var apps_spec_default = {
|
|
|
39293
39045
|
],
|
|
39294
39046
|
"x-group-title": "Developer"
|
|
39295
39047
|
}
|
|
39048
|
+
},
|
|
39049
|
+
"/apps/{id}/logs": {
|
|
39050
|
+
get: {
|
|
39051
|
+
description: "Lists a hosted app's server runtime logs, most recent first: console output, uncaught exceptions, and failed-request summaries captured on whop.app hosting. Logs are retained for 7 days.",
|
|
39052
|
+
operationId: "logs",
|
|
39053
|
+
parameters: [
|
|
39054
|
+
{
|
|
39055
|
+
description: "The ID of the app, which will look like app_*************.",
|
|
39056
|
+
in: "path",
|
|
39057
|
+
name: "id",
|
|
39058
|
+
required: true,
|
|
39059
|
+
schema: {
|
|
39060
|
+
type: "string"
|
|
39061
|
+
}
|
|
39062
|
+
},
|
|
39063
|
+
{
|
|
39064
|
+
description: "Only return logs from this build.",
|
|
39065
|
+
in: "query",
|
|
39066
|
+
name: "app_build_id",
|
|
39067
|
+
required: false,
|
|
39068
|
+
schema: {
|
|
39069
|
+
type: "string"
|
|
39070
|
+
}
|
|
39071
|
+
},
|
|
39072
|
+
{
|
|
39073
|
+
description: "Only return console lines of this level.",
|
|
39074
|
+
in: "query",
|
|
39075
|
+
name: "level",
|
|
39076
|
+
required: false,
|
|
39077
|
+
schema: {
|
|
39078
|
+
enum: [
|
|
39079
|
+
"log",
|
|
39080
|
+
"debug",
|
|
39081
|
+
"info",
|
|
39082
|
+
"warn",
|
|
39083
|
+
"error"
|
|
39084
|
+
],
|
|
39085
|
+
type: "string"
|
|
39086
|
+
}
|
|
39087
|
+
},
|
|
39088
|
+
{
|
|
39089
|
+
description: "Only return logs whose message contains this text (case-insensitive).",
|
|
39090
|
+
in: "query",
|
|
39091
|
+
name: "query",
|
|
39092
|
+
required: false,
|
|
39093
|
+
schema: {
|
|
39094
|
+
type: "string"
|
|
39095
|
+
}
|
|
39096
|
+
},
|
|
39097
|
+
{
|
|
39098
|
+
description: "Start of the time window as an ISO 8601 timestamp. Defaults to 7 days before created_before.",
|
|
39099
|
+
in: "query",
|
|
39100
|
+
name: "created_after",
|
|
39101
|
+
required: false,
|
|
39102
|
+
schema: {
|
|
39103
|
+
format: "date-time",
|
|
39104
|
+
type: "string"
|
|
39105
|
+
}
|
|
39106
|
+
},
|
|
39107
|
+
{
|
|
39108
|
+
description: "End of the time window as an ISO 8601 timestamp. Defaults to now.",
|
|
39109
|
+
in: "query",
|
|
39110
|
+
name: "created_before",
|
|
39111
|
+
required: false,
|
|
39112
|
+
schema: {
|
|
39113
|
+
format: "date-time",
|
|
39114
|
+
type: "string"
|
|
39115
|
+
}
|
|
39116
|
+
},
|
|
39117
|
+
{
|
|
39118
|
+
description: "The number of log lines to return (max 500).",
|
|
39119
|
+
in: "query",
|
|
39120
|
+
name: "first",
|
|
39121
|
+
required: false,
|
|
39122
|
+
schema: {
|
|
39123
|
+
type: "integer"
|
|
39124
|
+
}
|
|
39125
|
+
},
|
|
39126
|
+
{
|
|
39127
|
+
description: "A cursor for fetching logs after a previous page.",
|
|
39128
|
+
in: "query",
|
|
39129
|
+
name: "after",
|
|
39130
|
+
required: false,
|
|
39131
|
+
schema: {
|
|
39132
|
+
type: "string"
|
|
39133
|
+
}
|
|
39134
|
+
},
|
|
39135
|
+
{
|
|
39136
|
+
description: "A cursor for fetching logs before a later page.",
|
|
39137
|
+
in: "query",
|
|
39138
|
+
name: "before",
|
|
39139
|
+
required: false,
|
|
39140
|
+
schema: {
|
|
39141
|
+
type: "string"
|
|
39142
|
+
}
|
|
39143
|
+
}
|
|
39144
|
+
],
|
|
39145
|
+
responses: {
|
|
39146
|
+
"200": {
|
|
39147
|
+
content: {
|
|
39148
|
+
"application/json": {
|
|
39149
|
+
schema: {
|
|
39150
|
+
properties: {
|
|
39151
|
+
data: {
|
|
39152
|
+
items: {
|
|
39153
|
+
properties: {
|
|
39154
|
+
app_build_id: {
|
|
39155
|
+
type: "string"
|
|
39156
|
+
},
|
|
39157
|
+
app_id: {
|
|
39158
|
+
type: "string"
|
|
39159
|
+
},
|
|
39160
|
+
cpu_time_ms: {
|
|
39161
|
+
type: "integer"
|
|
39162
|
+
},
|
|
39163
|
+
created_at: {
|
|
39164
|
+
format: "date-time",
|
|
39165
|
+
type: "string"
|
|
39166
|
+
},
|
|
39167
|
+
level: {
|
|
39168
|
+
type: "string"
|
|
39169
|
+
},
|
|
39170
|
+
message: {
|
|
39171
|
+
type: "string"
|
|
39172
|
+
},
|
|
39173
|
+
outcome: {
|
|
39174
|
+
type: [
|
|
39175
|
+
"string",
|
|
39176
|
+
"null"
|
|
39177
|
+
]
|
|
39178
|
+
},
|
|
39179
|
+
request_id: {
|
|
39180
|
+
type: "string"
|
|
39181
|
+
},
|
|
39182
|
+
request_method: {
|
|
39183
|
+
type: [
|
|
39184
|
+
"string",
|
|
39185
|
+
"null"
|
|
39186
|
+
]
|
|
39187
|
+
},
|
|
39188
|
+
request_path: {
|
|
39189
|
+
type: [
|
|
39190
|
+
"string",
|
|
39191
|
+
"null"
|
|
39192
|
+
]
|
|
39193
|
+
},
|
|
39194
|
+
response_status: {
|
|
39195
|
+
type: [
|
|
39196
|
+
"integer",
|
|
39197
|
+
"null"
|
|
39198
|
+
]
|
|
39199
|
+
},
|
|
39200
|
+
source: {
|
|
39201
|
+
enum: [
|
|
39202
|
+
"console",
|
|
39203
|
+
"exception",
|
|
39204
|
+
"request"
|
|
39205
|
+
],
|
|
39206
|
+
type: "string"
|
|
39207
|
+
},
|
|
39208
|
+
stack: {
|
|
39209
|
+
type: [
|
|
39210
|
+
"string",
|
|
39211
|
+
"null"
|
|
39212
|
+
]
|
|
39213
|
+
},
|
|
39214
|
+
truncated: {
|
|
39215
|
+
type: "boolean"
|
|
39216
|
+
},
|
|
39217
|
+
wall_time_ms: {
|
|
39218
|
+
type: "integer"
|
|
39219
|
+
}
|
|
39220
|
+
},
|
|
39221
|
+
required: [
|
|
39222
|
+
"app_id",
|
|
39223
|
+
"app_build_id",
|
|
39224
|
+
"request_id",
|
|
39225
|
+
"created_at",
|
|
39226
|
+
"source",
|
|
39227
|
+
"level",
|
|
39228
|
+
"message"
|
|
39229
|
+
],
|
|
39230
|
+
type: "object"
|
|
39231
|
+
},
|
|
39232
|
+
type: "array"
|
|
39233
|
+
},
|
|
39234
|
+
page_info: {
|
|
39235
|
+
properties: {
|
|
39236
|
+
end_cursor: {
|
|
39237
|
+
type: [
|
|
39238
|
+
"string",
|
|
39239
|
+
"null"
|
|
39240
|
+
]
|
|
39241
|
+
},
|
|
39242
|
+
has_next_page: {
|
|
39243
|
+
type: "boolean"
|
|
39244
|
+
},
|
|
39245
|
+
has_previous_page: {
|
|
39246
|
+
type: "boolean"
|
|
39247
|
+
},
|
|
39248
|
+
start_cursor: {
|
|
39249
|
+
type: [
|
|
39250
|
+
"string",
|
|
39251
|
+
"null"
|
|
39252
|
+
]
|
|
39253
|
+
}
|
|
39254
|
+
},
|
|
39255
|
+
required: [
|
|
39256
|
+
"has_next_page",
|
|
39257
|
+
"has_previous_page"
|
|
39258
|
+
],
|
|
39259
|
+
type: "object"
|
|
39260
|
+
}
|
|
39261
|
+
},
|
|
39262
|
+
required: [
|
|
39263
|
+
"data",
|
|
39264
|
+
"page_info"
|
|
39265
|
+
],
|
|
39266
|
+
type: "object"
|
|
39267
|
+
}
|
|
39268
|
+
}
|
|
39269
|
+
},
|
|
39270
|
+
description: "logs listed"
|
|
39271
|
+
},
|
|
39272
|
+
"401": {
|
|
39273
|
+
$ref: "#/components/responses/Unauthorized",
|
|
39274
|
+
description: "missing or invalid authentication"
|
|
39275
|
+
},
|
|
39276
|
+
"403": {
|
|
39277
|
+
$ref: "#/components/responses/Forbidden",
|
|
39278
|
+
description: "missing the developer:logs:read scope"
|
|
39279
|
+
},
|
|
39280
|
+
"404": {
|
|
39281
|
+
$ref: "#/components/responses/NotFound",
|
|
39282
|
+
description: "app not found"
|
|
39283
|
+
}
|
|
39284
|
+
},
|
|
39285
|
+
security: [
|
|
39286
|
+
{
|
|
39287
|
+
bearerAuth: [
|
|
39288
|
+
"developer:logs:read"
|
|
39289
|
+
]
|
|
39290
|
+
}
|
|
39291
|
+
],
|
|
39292
|
+
summary: "List App Logs",
|
|
39293
|
+
tags: [
|
|
39294
|
+
"Apps"
|
|
39295
|
+
],
|
|
39296
|
+
"x-codeSamples": [
|
|
39297
|
+
{
|
|
39298
|
+
lang: "JavaScript",
|
|
39299
|
+
source: "import Whop from '@whop/sdk';\n\nconst client = new Whop({\n apiKey: process.env['WHOP_API_KEY'], // This is the default and can be omitted\n});\n\nconst response = await client.apps.logs('id');\n\nconsole.log(response.data);"
|
|
39300
|
+
}
|
|
39301
|
+
]
|
|
39302
|
+
}
|
|
39296
39303
|
}
|
|
39297
39304
|
},
|
|
39298
39305
|
components: {
|
|
@@ -39424,19 +39431,6 @@ var apps_spec_default = {
|
|
|
39424
39431
|
],
|
|
39425
39432
|
type: "object"
|
|
39426
39433
|
},
|
|
39427
|
-
AppViewTypes: {
|
|
39428
|
-
description: "The different types of an app view",
|
|
39429
|
-
enum: [
|
|
39430
|
-
"hub",
|
|
39431
|
-
"discover",
|
|
39432
|
-
"dash",
|
|
39433
|
-
"dashboard",
|
|
39434
|
-
"analytics",
|
|
39435
|
-
"skills",
|
|
39436
|
-
"openapi"
|
|
39437
|
-
],
|
|
39438
|
-
type: "string"
|
|
39439
|
-
},
|
|
39440
39434
|
AppBuild: {
|
|
39441
39435
|
description: "A versioned build artifact for a Whop React Native App, submitted for review and deployment to a specific platform.",
|
|
39442
39436
|
properties: {
|
|
@@ -39547,6 +39541,19 @@ var apps_spec_default = {
|
|
|
39547
39541
|
],
|
|
39548
39542
|
type: "string"
|
|
39549
39543
|
},
|
|
39544
|
+
AppViewTypes: {
|
|
39545
|
+
description: "The different types of an app view",
|
|
39546
|
+
enum: [
|
|
39547
|
+
"hub",
|
|
39548
|
+
"discover",
|
|
39549
|
+
"dash",
|
|
39550
|
+
"dashboard",
|
|
39551
|
+
"analytics",
|
|
39552
|
+
"skills",
|
|
39553
|
+
"openapi"
|
|
39554
|
+
],
|
|
39555
|
+
type: "string"
|
|
39556
|
+
},
|
|
39550
39557
|
AppListItem: {
|
|
39551
39558
|
description: "An app is an integration built on Whop. Apps can serve consumers as experiences within products, or serve companies as business tools.",
|
|
39552
39559
|
properties: {
|
|
@@ -40141,6 +40148,63 @@ var apps_spec_default = {
|
|
|
40141
40148
|
"hidden"
|
|
40142
40149
|
],
|
|
40143
40150
|
type: "string"
|
|
40151
|
+
},
|
|
40152
|
+
V1ErrorResponse: {
|
|
40153
|
+
properties: {
|
|
40154
|
+
error: {
|
|
40155
|
+
properties: {
|
|
40156
|
+
message: {
|
|
40157
|
+
description: "Human-readable error message.",
|
|
40158
|
+
type: "string"
|
|
40159
|
+
},
|
|
40160
|
+
type: {
|
|
40161
|
+
description: "Machine-readable error code.",
|
|
40162
|
+
type: "string"
|
|
40163
|
+
}
|
|
40164
|
+
},
|
|
40165
|
+
required: [
|
|
40166
|
+
"type",
|
|
40167
|
+
"message"
|
|
40168
|
+
],
|
|
40169
|
+
type: "object"
|
|
40170
|
+
}
|
|
40171
|
+
},
|
|
40172
|
+
required: [
|
|
40173
|
+
"error"
|
|
40174
|
+
],
|
|
40175
|
+
type: "object"
|
|
40176
|
+
}
|
|
40177
|
+
},
|
|
40178
|
+
responses: {
|
|
40179
|
+
Unauthorized: {
|
|
40180
|
+
content: {
|
|
40181
|
+
"application/json": {
|
|
40182
|
+
schema: {
|
|
40183
|
+
$ref: "#/components/schemas/V1ErrorResponse"
|
|
40184
|
+
}
|
|
40185
|
+
}
|
|
40186
|
+
},
|
|
40187
|
+
description: "Unauthorized"
|
|
40188
|
+
},
|
|
40189
|
+
Forbidden: {
|
|
40190
|
+
content: {
|
|
40191
|
+
"application/json": {
|
|
40192
|
+
schema: {
|
|
40193
|
+
$ref: "#/components/schemas/V1ErrorResponse"
|
|
40194
|
+
}
|
|
40195
|
+
}
|
|
40196
|
+
},
|
|
40197
|
+
description: "Forbidden"
|
|
40198
|
+
},
|
|
40199
|
+
NotFound: {
|
|
40200
|
+
content: {
|
|
40201
|
+
"application/json": {
|
|
40202
|
+
schema: {
|
|
40203
|
+
$ref: "#/components/schemas/V1ErrorResponse"
|
|
40204
|
+
}
|
|
40205
|
+
}
|
|
40206
|
+
},
|
|
40207
|
+
description: "Resource not found"
|
|
40144
40208
|
}
|
|
40145
40209
|
}
|
|
40146
40210
|
}
|
|
@@ -40217,7 +40281,99 @@ async function announceActionUrl(url, {
|
|
|
40217
40281
|
}
|
|
40218
40282
|
|
|
40219
40283
|
// src/auth/client.ts
|
|
40220
|
-
import { AuthenticationError, PermissionDeniedError
|
|
40284
|
+
import { AuthenticationError, PermissionDeniedError } from "@whop/sdk";
|
|
40285
|
+
|
|
40286
|
+
// src/lib/http.ts
|
|
40287
|
+
import { Whop } from "@whop/sdk";
|
|
40288
|
+
|
|
40289
|
+
// package.json
|
|
40290
|
+
var package_default = {
|
|
40291
|
+
name: "@whop/cli",
|
|
40292
|
+
version: "0.11.0",
|
|
40293
|
+
description: "The Whop CLI \u2014 build and manage Whop apps from your terminal. Human and agent friendly.",
|
|
40294
|
+
keywords: [
|
|
40295
|
+
"agent",
|
|
40296
|
+
"apps",
|
|
40297
|
+
"cli",
|
|
40298
|
+
"developer",
|
|
40299
|
+
"whop"
|
|
40300
|
+
],
|
|
40301
|
+
homepage: "https://whop.com/developers/",
|
|
40302
|
+
bugs: "https://github.com/whopio/whop-public-cli/issues",
|
|
40303
|
+
license: "MIT",
|
|
40304
|
+
repository: {
|
|
40305
|
+
type: "git",
|
|
40306
|
+
url: "git+https://github.com/whopio/whop-public-cli.git"
|
|
40307
|
+
},
|
|
40308
|
+
bin: {
|
|
40309
|
+
whop: "dist/index.js",
|
|
40310
|
+
"whop-cli": "dist/index.js"
|
|
40311
|
+
},
|
|
40312
|
+
files: [
|
|
40313
|
+
"dist",
|
|
40314
|
+
"skills"
|
|
40315
|
+
],
|
|
40316
|
+
type: "module",
|
|
40317
|
+
exports: {
|
|
40318
|
+
".": "./dist/index.js",
|
|
40319
|
+
"./vite": {
|
|
40320
|
+
types: "./dist/vite.d.ts",
|
|
40321
|
+
import: "./dist/vite.js"
|
|
40322
|
+
}
|
|
40323
|
+
},
|
|
40324
|
+
publishConfig: {
|
|
40325
|
+
access: "public"
|
|
40326
|
+
},
|
|
40327
|
+
scripts: {
|
|
40328
|
+
build: "tsx scripts/sync-spec.ts && pnpm run clean && tsup",
|
|
40329
|
+
"build:binary": "tsx scripts/build-binary.ts",
|
|
40330
|
+
clean: "rm -rf dist",
|
|
40331
|
+
dev: "tsx scripts/sync-spec.ts && tsx src/index.ts",
|
|
40332
|
+
start: "node dist/index.js",
|
|
40333
|
+
"check-types": "tsx scripts/sync-spec.ts && tsc --noEmit",
|
|
40334
|
+
lint: "oxlint .",
|
|
40335
|
+
"lint:fix": "oxlint --fix --fix-suggestions && oxfmt",
|
|
40336
|
+
"ci:lint": "oxlint -f github .",
|
|
40337
|
+
format: "oxfmt",
|
|
40338
|
+
"sync-spec": "tsx scripts/sync-spec.ts",
|
|
40339
|
+
test: "tsx --test scripts/*.test.ts"
|
|
40340
|
+
},
|
|
40341
|
+
dependencies: {
|
|
40342
|
+
"@clack/prompts": "^1.5.1",
|
|
40343
|
+
"@napi-rs/keyring": "^1.3.0",
|
|
40344
|
+
"@whop/sdk": "^0.0.40",
|
|
40345
|
+
chalk: "5.4.1",
|
|
40346
|
+
yaml: "2.8.0"
|
|
40347
|
+
},
|
|
40348
|
+
devDependencies: {
|
|
40349
|
+
"@types/node": "25.3.5",
|
|
40350
|
+
fflate: "0.8.2",
|
|
40351
|
+
incur: "github:whopio/incur#b0550076a7a66790e4d30db38103d8a91602b6cb",
|
|
40352
|
+
tsup: "8.5.0",
|
|
40353
|
+
tsx: "4.19.4",
|
|
40354
|
+
typescript: "5.9.3",
|
|
40355
|
+
vite: "7.3.6"
|
|
40356
|
+
},
|
|
40357
|
+
engines: {
|
|
40358
|
+
node: ">=22"
|
|
40359
|
+
},
|
|
40360
|
+
packageManager: "pnpm@10.23.0"
|
|
40361
|
+
};
|
|
40362
|
+
|
|
40363
|
+
// src/lib/http.ts
|
|
40364
|
+
var CLI_USER_AGENT = `Whop-CLI/${package_default.version}`;
|
|
40365
|
+
var whopFetch = (input, init) => {
|
|
40366
|
+
const headers = new Headers(
|
|
40367
|
+
init?.headers ?? (input instanceof Request ? input.headers : void 0)
|
|
40368
|
+
);
|
|
40369
|
+
headers.set("User-Agent", CLI_USER_AGENT);
|
|
40370
|
+
return fetch(input, { ...init, headers });
|
|
40371
|
+
};
|
|
40372
|
+
function newWhopSdk(options) {
|
|
40373
|
+
return new Whop({ ...options, fetch: whopFetch });
|
|
40374
|
+
}
|
|
40375
|
+
|
|
40376
|
+
// src/auth/client.ts
|
|
40221
40377
|
var DEFAULT_BASE_URL = "https://api.whop.com/api/v1";
|
|
40222
40378
|
function apiBaseUrl(env = process.env) {
|
|
40223
40379
|
return env.WHOP_API_BASE_URL?.trim() || DEFAULT_BASE_URL;
|
|
@@ -40226,7 +40382,7 @@ function oauthBaseUrl(env = process.env) {
|
|
|
40226
40382
|
return apiBaseUrl(env).replace(/\/+$/, "").replace(/\/api\/v1$/, "");
|
|
40227
40383
|
}
|
|
40228
40384
|
async function validateApiKey(apiKey, baseUrl = apiBaseUrl()) {
|
|
40229
|
-
const whop =
|
|
40385
|
+
const whop = newWhopSdk({ apiKey, baseURL: baseUrl });
|
|
40230
40386
|
try {
|
|
40231
40387
|
const account = await whop.accounts.me();
|
|
40232
40388
|
return {
|
|
@@ -40340,7 +40496,7 @@ import { createRequire as createRequire2 } from "module";
|
|
|
40340
40496
|
// src/lib/build-info.ts
|
|
40341
40497
|
var buildChannel = true ? "npm" : "dev";
|
|
40342
40498
|
var buildTarget = typeof WHOP_CLI_TARGET === "string" ? WHOP_CLI_TARGET : null;
|
|
40343
|
-
var buildVersion = true ? "0.
|
|
40499
|
+
var buildVersion = true ? "0.11.0" : null;
|
|
40344
40500
|
function isHomebrewPath(execPath, env = process.env) {
|
|
40345
40501
|
if (/[/\\](Cellar|homebrew|linuxbrew)[/\\]/.test(execPath)) return true;
|
|
40346
40502
|
const prefix = env.HOMEBREW_PREFIX?.trim();
|
|
@@ -40490,7 +40646,6 @@ function deleteSecret(name, env = process.env) {
|
|
|
40490
40646
|
|
|
40491
40647
|
// src/auth/oauth.ts
|
|
40492
40648
|
import { createHash as createHash2, randomBytes } from "crypto";
|
|
40493
|
-
import { Whop as Whop2 } from "@whop/sdk";
|
|
40494
40649
|
|
|
40495
40650
|
// src/auth/callback-server.ts
|
|
40496
40651
|
import { createServer } from "http";
|
|
@@ -40933,7 +41088,7 @@ function generateNonce() {
|
|
|
40933
41088
|
return randomBytes(16).toString("base64url");
|
|
40934
41089
|
}
|
|
40935
41090
|
async function requestToken(params) {
|
|
40936
|
-
const res = await
|
|
41091
|
+
const res = await whopFetch(`${oauthBaseUrl()}/oauth/token`, {
|
|
40937
41092
|
method: "POST",
|
|
40938
41093
|
headers: { "Content-Type": "application/x-www-form-urlencoded" },
|
|
40939
41094
|
body: new URLSearchParams(params).toString()
|
|
@@ -40989,7 +41144,7 @@ var OAuthError = class extends Error {
|
|
|
40989
41144
|
var defaultOAuthLoginDependencies = {
|
|
40990
41145
|
startCallbackServer,
|
|
40991
41146
|
exchangeCode,
|
|
40992
|
-
fetch,
|
|
41147
|
+
fetch: whopFetch,
|
|
40993
41148
|
listOAuthAccounts,
|
|
40994
41149
|
listProfiles,
|
|
40995
41150
|
upsertProfile,
|
|
@@ -40999,7 +41154,7 @@ var defaultOAuthLoginDependencies = {
|
|
|
40999
41154
|
printInfo
|
|
41000
41155
|
};
|
|
41001
41156
|
async function listOAuthAccounts(accessToken, opts = {}) {
|
|
41002
|
-
const whop =
|
|
41157
|
+
const whop = newWhopSdk({ apiKey: accessToken, baseURL: apiBaseUrl() });
|
|
41003
41158
|
const MAX_ACCOUNTS = 1e3;
|
|
41004
41159
|
const accounts = [];
|
|
41005
41160
|
for await (const account of whop.companies.list({ first: 100 })) {
|
|
@@ -41081,13 +41236,13 @@ async function prepareOAuthLogin(input, dependencies = defaultOAuthLoginDependen
|
|
|
41081
41236
|
error instanceof Error ? error.message : "OAuth callback failed."
|
|
41082
41237
|
);
|
|
41083
41238
|
}
|
|
41084
|
-
const
|
|
41239
|
+
const spinner6 = input.interactive ? dependencies.startSpinner("Exchanging authorization code\u2026") : null;
|
|
41085
41240
|
let tokens;
|
|
41086
41241
|
try {
|
|
41087
41242
|
tokens = await dependencies.exchangeCode(code, verifier);
|
|
41088
|
-
|
|
41243
|
+
spinner6?.stop("Authorized.");
|
|
41089
41244
|
} catch (error) {
|
|
41090
|
-
|
|
41245
|
+
spinner6?.stop("Token exchange failed.");
|
|
41091
41246
|
throw new OAuthError(
|
|
41092
41247
|
"TOKEN_EXCHANGE_FAILED",
|
|
41093
41248
|
error instanceof Error ? error.message : "Token exchange failed."
|
|
@@ -41140,9 +41295,9 @@ async function performOAuthLogin(input, dependencies = defaultOAuthLoginDependen
|
|
|
41140
41295
|
{ ...input, interactive: true },
|
|
41141
41296
|
dependencies
|
|
41142
41297
|
);
|
|
41143
|
-
const
|
|
41298
|
+
const spinner6 = dependencies.startSpinner("Opening browser\u2026");
|
|
41144
41299
|
dependencies.openBrowser(login.authorizationUrl);
|
|
41145
|
-
|
|
41300
|
+
spinner6.stop("Waiting for authorization in your browser\u2026");
|
|
41146
41301
|
dependencies.printInfo(
|
|
41147
41302
|
`If your browser didn't open, visit:
|
|
41148
41303
|
${login.authorizationUrl}`
|
|
@@ -41516,7 +41671,11 @@ function createWhopFetch() {
|
|
|
41516
41671
|
if (!headers.has(API_VERSION_HEADER)) {
|
|
41517
41672
|
headers.set(API_VERSION_HEADER, API_VERSION_DATE);
|
|
41518
41673
|
}
|
|
41519
|
-
const res = await
|
|
41674
|
+
const res = await whopFetch(target, {
|
|
41675
|
+
method: request2.method,
|
|
41676
|
+
headers,
|
|
41677
|
+
body
|
|
41678
|
+
});
|
|
41520
41679
|
if (res.ok) {
|
|
41521
41680
|
if (method === "POST" || method === "PATCH") {
|
|
41522
41681
|
const text5 = await res.text();
|
|
@@ -41637,7 +41796,7 @@ var groups_default = [
|
|
|
41637
41796
|
{
|
|
41638
41797
|
name: "verifications",
|
|
41639
41798
|
tag: "Verifications",
|
|
41640
|
-
description: "
|
|
41799
|
+
description: "Legal identity required before payouts and card issuing.",
|
|
41641
41800
|
group: "Money"
|
|
41642
41801
|
},
|
|
41643
41802
|
{
|
|
@@ -41739,16 +41898,16 @@ var API_GROUPS = groups_default.filter(
|
|
|
41739
41898
|
);
|
|
41740
41899
|
|
|
41741
41900
|
// src/apps/commands.ts
|
|
41742
|
-
import {
|
|
41743
|
-
import { spawnSync as spawnSync3 } from "child_process";
|
|
41901
|
+
import { spinner as spinner4 } from "@clack/prompts";
|
|
41744
41902
|
import { createHash as createHash3 } from "crypto";
|
|
41745
|
-
import { existsSync as
|
|
41746
|
-
import {
|
|
41747
|
-
import
|
|
41903
|
+
import { existsSync as existsSync7, mkdtempSync as mkdtempSync3, readFileSync as readFileSync7, rmSync as rmSync4 } from "fs";
|
|
41904
|
+
import { tmpdir as tmpdir3 } from "os";
|
|
41905
|
+
import { join as join10, relative as relative2, resolve as resolve8 } from "path";
|
|
41906
|
+
import chalk2 from "chalk";
|
|
41748
41907
|
|
|
41749
41908
|
// src/journey/client.ts
|
|
41750
41909
|
var PLACEHOLDER = "https://api.whop.com";
|
|
41751
|
-
var
|
|
41910
|
+
var proxyFetch = createWhopFetch();
|
|
41752
41911
|
function apiErrorMessage(payload) {
|
|
41753
41912
|
if (!payload || typeof payload !== "object") return void 0;
|
|
41754
41913
|
const response = payload;
|
|
@@ -41763,14 +41922,14 @@ function apiErrorMessage(payload) {
|
|
|
41763
41922
|
async function makeWhopRequest(method, path8, body, accessToken) {
|
|
41764
41923
|
const init = { method };
|
|
41765
41924
|
if (body !== void 0) init.body = JSON.stringify(body);
|
|
41766
|
-
const res = accessToken ? await
|
|
41925
|
+
const res = accessToken ? await whopFetch(`${apiBaseUrl().replace(/\/$/, "")}${path8}`, {
|
|
41767
41926
|
...init,
|
|
41768
41927
|
headers: {
|
|
41769
41928
|
Authorization: `Bearer ${accessToken}`,
|
|
41770
41929
|
"Api-Version-Date": API_VERSION_DATE,
|
|
41771
41930
|
...body !== void 0 ? { "Content-Type": "application/json" } : {}
|
|
41772
41931
|
}
|
|
41773
|
-
}) : await
|
|
41932
|
+
}) : await proxyFetch(new Request(`${PLACEHOLDER}${path8}`, init));
|
|
41774
41933
|
if (!res.ok) {
|
|
41775
41934
|
const payload = await res.json().catch(() => null);
|
|
41776
41935
|
throw new Error(apiErrorMessage(payload) ?? res.statusText);
|
|
@@ -41810,14 +41969,6 @@ async function updateAppRoute(appId, route) {
|
|
|
41810
41969
|
async function updateAppSecrets(appId, secrets) {
|
|
41811
41970
|
return makeWhopRequest("PATCH", `/apps/${appId}`, { secrets });
|
|
41812
41971
|
}
|
|
41813
|
-
async function listAppLogs(appId, params) {
|
|
41814
|
-
const query = new URLSearchParams();
|
|
41815
|
-
for (const [key, value] of Object.entries(params)) {
|
|
41816
|
-
if (value !== void 0 && value !== "") query.set(key, String(value));
|
|
41817
|
-
}
|
|
41818
|
-
const qs = query.size > 0 ? `?${query}` : "";
|
|
41819
|
-
return makeWhopRequest("GET", `/apps/${appId}/logs${qs}`);
|
|
41820
|
-
}
|
|
41821
41972
|
async function createAppBuild(input) {
|
|
41822
41973
|
return makeWhopRequest("POST", "/app_builds", {
|
|
41823
41974
|
app_id: input.app_id,
|
|
@@ -41883,7 +42034,7 @@ async function waitForBuildPromotion(buildId, timeoutMs = 3e4) {
|
|
|
41883
42034
|
return latest;
|
|
41884
42035
|
}
|
|
41885
42036
|
function sleep(ms) {
|
|
41886
|
-
return new Promise((
|
|
42037
|
+
return new Promise((resolve9) => setTimeout(resolve9, ms));
|
|
41887
42038
|
}
|
|
41888
42039
|
|
|
41889
42040
|
// src/apps/config.ts
|
|
@@ -41926,8 +42077,176 @@ function slugify3(name) {
|
|
|
41926
42077
|
return name.toLowerCase().normalize("NFKD").replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "").slice(0, 63);
|
|
41927
42078
|
}
|
|
41928
42079
|
|
|
41929
|
-
// src/apps/
|
|
42080
|
+
// src/apps/git.ts
|
|
41930
42081
|
import { spawnSync } from "child_process";
|
|
42082
|
+
import { mkdtempSync, realpathSync as realpathSync2, rmSync as rmSync2 } from "fs";
|
|
42083
|
+
import { tmpdir } from "os";
|
|
42084
|
+
import { join as join5 } from "path";
|
|
42085
|
+
var SPAWN_VIA_SHELL = process.platform === "win32";
|
|
42086
|
+
function runGit(dir, args, env = {}) {
|
|
42087
|
+
const result = spawnSync("git", args, {
|
|
42088
|
+
cwd: dir,
|
|
42089
|
+
encoding: "utf-8",
|
|
42090
|
+
shell: SPAWN_VIA_SHELL,
|
|
42091
|
+
env: { ...process.env, ...env },
|
|
42092
|
+
maxBuffer: 128 * 1024 * 1024
|
|
42093
|
+
});
|
|
42094
|
+
return {
|
|
42095
|
+
status: result.status,
|
|
42096
|
+
stdout: (result.stdout ?? "").trim(),
|
|
42097
|
+
stderr: (result.stderr ?? "").trim(),
|
|
42098
|
+
failed: Boolean(result.error) || result.status !== 0
|
|
42099
|
+
};
|
|
42100
|
+
}
|
|
42101
|
+
function mustGit(dir, args, env = {}) {
|
|
42102
|
+
const result = runGit(dir, args, env);
|
|
42103
|
+
if (result.failed) {
|
|
42104
|
+
throw new Error(
|
|
42105
|
+
`\`git ${args.join(" ")}\` failed${result.status != null ? ` (exit ${result.status})` : ""}: ${result.stderr || result.stdout}`
|
|
42106
|
+
);
|
|
42107
|
+
}
|
|
42108
|
+
return result.stdout;
|
|
42109
|
+
}
|
|
42110
|
+
function gitInstalled() {
|
|
42111
|
+
const result = spawnSync("git", ["--version"], {
|
|
42112
|
+
stdio: "ignore",
|
|
42113
|
+
shell: SPAWN_VIA_SHELL
|
|
42114
|
+
});
|
|
42115
|
+
return !result.error && result.status === 0;
|
|
42116
|
+
}
|
|
42117
|
+
function repoToplevel(dir) {
|
|
42118
|
+
const result = runGit(dir, ["rev-parse", "--show-toplevel"]);
|
|
42119
|
+
return result.failed ? null : result.stdout;
|
|
42120
|
+
}
|
|
42121
|
+
function repoStatus(dir) {
|
|
42122
|
+
const toplevel = repoToplevel(dir);
|
|
42123
|
+
if (toplevel === null) return { kind: "none" };
|
|
42124
|
+
return realpathSync2(toplevel) === realpathSync2(dir) ? { kind: "root" } : { kind: "nested", toplevel };
|
|
42125
|
+
}
|
|
42126
|
+
function gitInit(dir) {
|
|
42127
|
+
mustGit(dir, ["init"]);
|
|
42128
|
+
}
|
|
42129
|
+
function hasCommits(dir) {
|
|
42130
|
+
return !runGit(dir, ["rev-parse", "-q", "--verify", "HEAD"]).failed;
|
|
42131
|
+
}
|
|
42132
|
+
function isDirty(dir) {
|
|
42133
|
+
return mustGit(dir, ["status", "--porcelain"]).length > 0;
|
|
42134
|
+
}
|
|
42135
|
+
function remoteRef(appId) {
|
|
42136
|
+
return `refs/whop/remote/${appId}`;
|
|
42137
|
+
}
|
|
42138
|
+
function refCommit(dir, ref) {
|
|
42139
|
+
const result = runGit(dir, ["rev-parse", "-q", "--verify", ref]);
|
|
42140
|
+
return result.failed ? null : result.stdout;
|
|
42141
|
+
}
|
|
42142
|
+
function identityArgs(dir) {
|
|
42143
|
+
const email = runGit(dir, ["config", "--get", "user.email"]);
|
|
42144
|
+
if (!email.failed && email.stdout) return [];
|
|
42145
|
+
return [
|
|
42146
|
+
"-c",
|
|
42147
|
+
"user.name=whop CLI",
|
|
42148
|
+
"-c",
|
|
42149
|
+
"user.email=cli@whop.com"
|
|
42150
|
+
];
|
|
42151
|
+
}
|
|
42152
|
+
function commitAll(dir, message) {
|
|
42153
|
+
mustGit(dir, ["add", "-A"]);
|
|
42154
|
+
mustGit(dir, [...identityArgs(dir), "commit", "-m", message, "--no-verify"]);
|
|
42155
|
+
}
|
|
42156
|
+
function commitSnapshot(repoDir, snapshotDir, appId, message, options = {}) {
|
|
42157
|
+
const ref = remoteRef(appId);
|
|
42158
|
+
const gitDir = mustGit(repoDir, ["rev-parse", "--absolute-git-dir"]);
|
|
42159
|
+
const scratch = mkdtempSync(join5(tmpdir(), "whop-git-"));
|
|
42160
|
+
try {
|
|
42161
|
+
const env = {
|
|
42162
|
+
GIT_DIR: gitDir,
|
|
42163
|
+
GIT_WORK_TREE: snapshotDir,
|
|
42164
|
+
GIT_INDEX_FILE: join5(scratch, "index")
|
|
42165
|
+
};
|
|
42166
|
+
mustGit(snapshotDir, ["add", "-A", "-f", "."], env);
|
|
42167
|
+
const tree = mustGit(snapshotDir, ["write-tree"], env);
|
|
42168
|
+
const prev = refCommit(repoDir, ref);
|
|
42169
|
+
const prevTree = prev ? mustGit(repoDir, ["rev-parse", `${prev}^{tree}`]) : null;
|
|
42170
|
+
if (prev && tree === prevTree) return { commit: prev, changed: false };
|
|
42171
|
+
const parents = [prev].filter(Boolean);
|
|
42172
|
+
if (options.deriveFromHead) {
|
|
42173
|
+
const head = headCommit(repoDir);
|
|
42174
|
+
if (head && !parents.includes(head) && deletesNothingFrom(repoDir, head, tree)) {
|
|
42175
|
+
parents.push(head);
|
|
42176
|
+
}
|
|
42177
|
+
}
|
|
42178
|
+
const commit = mustGit(repoDir, [
|
|
42179
|
+
...identityArgs(repoDir),
|
|
42180
|
+
"commit-tree",
|
|
42181
|
+
tree,
|
|
42182
|
+
...parents.flatMap((parent) => ["-p", parent]),
|
|
42183
|
+
"-m",
|
|
42184
|
+
message
|
|
42185
|
+
]);
|
|
42186
|
+
mustGit(repoDir, ["update-ref", ref, commit]);
|
|
42187
|
+
return { commit, changed: true };
|
|
42188
|
+
} finally {
|
|
42189
|
+
rmSync2(scratch, { recursive: true, force: true });
|
|
42190
|
+
}
|
|
42191
|
+
}
|
|
42192
|
+
function deletesNothingFrom(dir, commit, tree) {
|
|
42193
|
+
return !runGit(dir, ["diff", "--quiet", "--diff-filter=D", commit, tree]).failed;
|
|
42194
|
+
}
|
|
42195
|
+
function headCommit(dir) {
|
|
42196
|
+
const result = runGit(dir, ["rev-parse", "-q", "--verify", "HEAD"]);
|
|
42197
|
+
return result.failed ? null : result.stdout;
|
|
42198
|
+
}
|
|
42199
|
+
function nothingToMerge(dir, commit) {
|
|
42200
|
+
const base = runGit(dir, ["merge-base", "HEAD", commit]);
|
|
42201
|
+
if (base.failed) return false;
|
|
42202
|
+
return !runGit(dir, ["diff", "--quiet", base.stdout, commit]).failed;
|
|
42203
|
+
}
|
|
42204
|
+
function isMerged(dir, commit) {
|
|
42205
|
+
return !runGit(dir, ["merge-base", "--is-ancestor", commit, "HEAD"]).failed;
|
|
42206
|
+
}
|
|
42207
|
+
function mergeSnapshot(dir, commit, message) {
|
|
42208
|
+
const result = runGit(dir, [
|
|
42209
|
+
...identityArgs(dir),
|
|
42210
|
+
"merge",
|
|
42211
|
+
"--allow-unrelated-histories",
|
|
42212
|
+
"-m",
|
|
42213
|
+
message,
|
|
42214
|
+
commit
|
|
42215
|
+
]);
|
|
42216
|
+
if (!result.failed) return { status: "merged" };
|
|
42217
|
+
const conflicts = runGit(dir, [
|
|
42218
|
+
"diff",
|
|
42219
|
+
"--name-only",
|
|
42220
|
+
"--diff-filter=U"
|
|
42221
|
+
]);
|
|
42222
|
+
if (!conflicts.failed && conflicts.stdout) {
|
|
42223
|
+
return { status: "conflicts", files: conflicts.stdout.split("\n") };
|
|
42224
|
+
}
|
|
42225
|
+
throw new Error(
|
|
42226
|
+
`\`git merge\` failed: ${result.stderr || result.stdout}`
|
|
42227
|
+
);
|
|
42228
|
+
}
|
|
42229
|
+
function resetHard(dir, commit) {
|
|
42230
|
+
mustGit(dir, ["reset", "--hard", commit]);
|
|
42231
|
+
}
|
|
42232
|
+
function stashPush(dir, message) {
|
|
42233
|
+
const before = runGit(dir, ["stash", "list"]).stdout;
|
|
42234
|
+
mustGit(dir, ["stash", "push", "--include-untracked", "--message", message]);
|
|
42235
|
+
const after = runGit(dir, ["stash", "list"]).stdout;
|
|
42236
|
+
return after !== before ? "stashed" : "clean";
|
|
42237
|
+
}
|
|
42238
|
+
function stashPop(dir) {
|
|
42239
|
+
return !runGit(dir, ["stash", "pop"]).failed;
|
|
42240
|
+
}
|
|
42241
|
+
|
|
42242
|
+
// src/apps/init.ts
|
|
42243
|
+
import { spinner as spinner3 } from "@clack/prompts";
|
|
42244
|
+
import { existsSync as existsSync6, readdirSync as readdirSync3 } from "fs";
|
|
42245
|
+
import { resolve as resolve6 } from "path";
|
|
42246
|
+
import chalk from "chalk";
|
|
42247
|
+
|
|
42248
|
+
// src/apps/source-archive.ts
|
|
42249
|
+
import { spawnSync as spawnSync2 } from "child_process";
|
|
41931
42250
|
import {
|
|
41932
42251
|
mkdirSync as mkdirSync3,
|
|
41933
42252
|
readdirSync as readdirSync2,
|
|
@@ -41935,7 +42254,7 @@ import {
|
|
|
41935
42254
|
statSync,
|
|
41936
42255
|
writeFileSync as writeFileSync3
|
|
41937
42256
|
} from "fs";
|
|
41938
|
-
import { dirname as dirname3, join as
|
|
42257
|
+
import { dirname as dirname3, join as join6, resolve as resolve5, sep } from "path";
|
|
41939
42258
|
|
|
41940
42259
|
// ../../node_modules/.pnpm/fflate@0.8.2/node_modules/fflate/esm/index.mjs
|
|
41941
42260
|
import { createRequire as createRequire3 } from "module";
|
|
@@ -42929,7 +43248,7 @@ function isExcluded(path8) {
|
|
|
42929
43248
|
return segments.slice(0, -1).some((dir) => EXCLUDED_DIRS.has(dir)) || isExcludedFile(segments[segments.length - 1]);
|
|
42930
43249
|
}
|
|
42931
43250
|
function listGitFiles(projectDir) {
|
|
42932
|
-
const listed =
|
|
43251
|
+
const listed = spawnSync2(
|
|
42933
43252
|
"git",
|
|
42934
43253
|
["ls-files", "--cached", "--others", "--exclude-standard", "-z"],
|
|
42935
43254
|
{ cwd: projectDir, encoding: "utf-8", maxBuffer: 128 * 1024 * 1024 }
|
|
@@ -42938,7 +43257,7 @@ function listGitFiles(projectDir) {
|
|
|
42938
43257
|
return [...new Set(listed.stdout.split("\0").filter(Boolean))].filter(
|
|
42939
43258
|
(path8) => {
|
|
42940
43259
|
try {
|
|
42941
|
-
return statSync(
|
|
43260
|
+
return statSync(join6(projectDir, path8)).isFile();
|
|
42942
43261
|
} catch {
|
|
42943
43262
|
return false;
|
|
42944
43263
|
}
|
|
@@ -42947,7 +43266,7 @@ function listGitFiles(projectDir) {
|
|
|
42947
43266
|
}
|
|
42948
43267
|
function walkProject(projectDir, prefix = "") {
|
|
42949
43268
|
const files = [];
|
|
42950
|
-
for (const entry of readdirSync2(
|
|
43269
|
+
for (const entry of readdirSync2(join6(projectDir, prefix), {
|
|
42951
43270
|
withFileTypes: true
|
|
42952
43271
|
})) {
|
|
42953
43272
|
const path8 = prefix ? `${prefix}/${entry.name}` : entry.name;
|
|
@@ -42969,7 +43288,7 @@ function packSourceArchive(projectDir) {
|
|
|
42969
43288
|
}
|
|
42970
43289
|
const contents = {};
|
|
42971
43290
|
for (const path8 of paths) {
|
|
42972
|
-
contents[path8] = readFileSync4(
|
|
43291
|
+
contents[path8] = readFileSync4(join6(projectDir, path8));
|
|
42973
43292
|
}
|
|
42974
43293
|
return {
|
|
42975
43294
|
zip: zipSync(contents, { level: 9, mem: 12 }),
|
|
@@ -42994,10 +43313,10 @@ function extractSourceArchive(zip, targetDir) {
|
|
|
42994
43313
|
}
|
|
42995
43314
|
|
|
42996
43315
|
// src/apps/scaffold.ts
|
|
42997
|
-
import { spawnSync as
|
|
43316
|
+
import { spawnSync as spawnSync3 } from "child_process";
|
|
42998
43317
|
import { existsSync as existsSync4, readFileSync as readFileSync5, writeFileSync as writeFileSync4 } from "fs";
|
|
42999
|
-
import { basename as basename2, join as
|
|
43000
|
-
var
|
|
43318
|
+
import { basename as basename2, join as join7 } from "path";
|
|
43319
|
+
var SPAWN_VIA_SHELL2 = process.platform === "win32";
|
|
43001
43320
|
var DLX_RUNNERS = {
|
|
43002
43321
|
npm: ["npx", "-y"],
|
|
43003
43322
|
pnpm: ["pnpm", "dlx"],
|
|
@@ -43010,9 +43329,9 @@ var PM_PRIORITY = [
|
|
|
43010
43329
|
"npm"
|
|
43011
43330
|
];
|
|
43012
43331
|
function isPmInstalled(pm) {
|
|
43013
|
-
const result =
|
|
43332
|
+
const result = spawnSync3(pm, ["--version"], {
|
|
43014
43333
|
stdio: "ignore",
|
|
43015
|
-
shell:
|
|
43334
|
+
shell: SPAWN_VIA_SHELL2
|
|
43016
43335
|
});
|
|
43017
43336
|
return !result.error && result.status === 0;
|
|
43018
43337
|
}
|
|
@@ -43044,15 +43363,6 @@ var TANSTACK_CREATE_FLAGS = [
|
|
|
43044
43363
|
"--no-examples",
|
|
43045
43364
|
"--non-interactive"
|
|
43046
43365
|
];
|
|
43047
|
-
function tanstackCreateCommand(pm, projectName) {
|
|
43048
|
-
return [
|
|
43049
|
-
...DLX_RUNNERS[pm],
|
|
43050
|
-
"@tanstack/cli@latest",
|
|
43051
|
-
"create",
|
|
43052
|
-
projectName,
|
|
43053
|
-
...TANSTACK_CREATE_FLAGS
|
|
43054
|
-
].join(" ");
|
|
43055
|
-
}
|
|
43056
43366
|
function outputTail(result) {
|
|
43057
43367
|
const combined = [result.stdout, result.stderr].filter(Boolean).join("\n").trim();
|
|
43058
43368
|
if (!combined) return "";
|
|
@@ -43062,7 +43372,7 @@ ${combined.split("\n").slice(-15).join("\n")}`;
|
|
|
43062
43372
|
}
|
|
43063
43373
|
function runTanstackCreate(options) {
|
|
43064
43374
|
const [runner, ...runnerArgs] = DLX_RUNNERS[options.pm];
|
|
43065
|
-
const result =
|
|
43375
|
+
const result = spawnSync3(
|
|
43066
43376
|
runner,
|
|
43067
43377
|
[
|
|
43068
43378
|
...runnerArgs,
|
|
@@ -43076,7 +43386,7 @@ function runTanstackCreate(options) {
|
|
|
43076
43386
|
{
|
|
43077
43387
|
stdio: ["ignore", "pipe", "pipe"],
|
|
43078
43388
|
encoding: "utf-8",
|
|
43079
|
-
shell:
|
|
43389
|
+
shell: SPAWN_VIA_SHELL2
|
|
43080
43390
|
}
|
|
43081
43391
|
);
|
|
43082
43392
|
if (result.error) throw result.error;
|
|
@@ -43093,7 +43403,7 @@ var VITE_CONFIG_NAMES = [
|
|
|
43093
43403
|
"vite.config.mjs"
|
|
43094
43404
|
];
|
|
43095
43405
|
function findViteConfig(dir) {
|
|
43096
|
-
return VITE_CONFIG_NAMES.map((name) =>
|
|
43406
|
+
return VITE_CONFIG_NAMES.map((name) => join7(dir, name)).find(
|
|
43097
43407
|
(path8) => existsSync4(path8)
|
|
43098
43408
|
);
|
|
43099
43409
|
}
|
|
@@ -43109,7 +43419,7 @@ function whopifyProject(options) {
|
|
|
43109
43419
|
return { manualSteps };
|
|
43110
43420
|
}
|
|
43111
43421
|
function patchPackageJson(dir, manualSteps) {
|
|
43112
|
-
const file =
|
|
43422
|
+
const file = join7(dir, "package.json");
|
|
43113
43423
|
let pkg;
|
|
43114
43424
|
try {
|
|
43115
43425
|
pkg = JSON.parse(readFileSync5(file, "utf-8"));
|
|
@@ -43166,7 +43476,7 @@ ${content}`;
|
|
|
43166
43476
|
}
|
|
43167
43477
|
}
|
|
43168
43478
|
function patchWranglerConfig(dir, route, manualSteps) {
|
|
43169
|
-
const file = WRANGLER_CONFIG_NAMES.map((name) =>
|
|
43479
|
+
const file = WRANGLER_CONFIG_NAMES.map((name) => join7(dir, name)).find(
|
|
43170
43480
|
(path8) => existsSync4(path8)
|
|
43171
43481
|
);
|
|
43172
43482
|
if (!file) {
|
|
@@ -43224,6 +43534,548 @@ function replaceTopLevelName(content, route) {
|
|
|
43224
43534
|
return null;
|
|
43225
43535
|
}
|
|
43226
43536
|
|
|
43537
|
+
// src/apps/shared.ts
|
|
43538
|
+
import { isCancel as isCancel3, select as select2, spinner as spinner2, text as text2 } from "@clack/prompts";
|
|
43539
|
+
import { spawnSync as spawnSync4 } from "child_process";
|
|
43540
|
+
import { existsSync as existsSync5, readFileSync as readFileSync6 } from "fs";
|
|
43541
|
+
import { join as join8 } from "path";
|
|
43542
|
+
var CommandAbort = class extends Error {
|
|
43543
|
+
constructor(options) {
|
|
43544
|
+
super(options.message);
|
|
43545
|
+
this.options = options;
|
|
43546
|
+
}
|
|
43547
|
+
};
|
|
43548
|
+
function log2(c2, message) {
|
|
43549
|
+
if (!c2.agent) console.log(message);
|
|
43550
|
+
}
|
|
43551
|
+
function cancelled() {
|
|
43552
|
+
throw new CommandAbort({
|
|
43553
|
+
code: "CANCELLED",
|
|
43554
|
+
message: "Cancelled.",
|
|
43555
|
+
exitCode: 130
|
|
43556
|
+
});
|
|
43557
|
+
}
|
|
43558
|
+
async function promptText(message, placeholder, initialValue) {
|
|
43559
|
+
const value = await text2({ message, placeholder, initialValue });
|
|
43560
|
+
if (isCancel3(value)) return null;
|
|
43561
|
+
return String(value);
|
|
43562
|
+
}
|
|
43563
|
+
async function promptSelect(message, options) {
|
|
43564
|
+
const choice = await select2({ message, options });
|
|
43565
|
+
if (isCancel3(choice)) cancelled();
|
|
43566
|
+
return String(choice);
|
|
43567
|
+
}
|
|
43568
|
+
async function promptForNameAndRoute() {
|
|
43569
|
+
const name = await promptText("What is your app called?", "My Site");
|
|
43570
|
+
if (name === null) cancelled();
|
|
43571
|
+
const slug = slugify3(name);
|
|
43572
|
+
const routeInput = await promptText(
|
|
43573
|
+
"Which route should it live at? (your-route.whop.app)",
|
|
43574
|
+
slug,
|
|
43575
|
+
slug
|
|
43576
|
+
);
|
|
43577
|
+
if (routeInput === null) cancelled();
|
|
43578
|
+
const route = slugify3(routeInput);
|
|
43579
|
+
if (!route) {
|
|
43580
|
+
throw new CommandAbort({
|
|
43581
|
+
code: "ROUTE_REQUIRED",
|
|
43582
|
+
message: `Couldn't derive a route from "${routeInput}" \u2014 use letters and numbers (e.g. my-site).`,
|
|
43583
|
+
retryable: true
|
|
43584
|
+
});
|
|
43585
|
+
}
|
|
43586
|
+
return { name, route };
|
|
43587
|
+
}
|
|
43588
|
+
function ensurePackageManager() {
|
|
43589
|
+
const pm = detectPackageManager();
|
|
43590
|
+
if (pm) return pm;
|
|
43591
|
+
throw new CommandAbort({
|
|
43592
|
+
code: "NO_PACKAGE_MANAGER",
|
|
43593
|
+
message: `No package manager found (looked for bun, pnpm, npm). Install one and retry:
|
|
43594
|
+
|
|
43595
|
+
bun (recommended): ${pmInstallHint("bun")}
|
|
43596
|
+
pnpm: ${pmInstallHint("pnpm")}
|
|
43597
|
+
npm: ${pmInstallHint("npm")}`
|
|
43598
|
+
});
|
|
43599
|
+
}
|
|
43600
|
+
var SPAWN_VIA_SHELL3 = process.platform === "win32";
|
|
43601
|
+
function detectProjectPackageManager(projectDir) {
|
|
43602
|
+
if (existsSync5(join8(projectDir, "bun.lock")) || existsSync5(join8(projectDir, "bun.lockb")))
|
|
43603
|
+
return "bun";
|
|
43604
|
+
if (existsSync5(join8(projectDir, "pnpm-lock.yaml"))) return "pnpm";
|
|
43605
|
+
if (existsSync5(join8(projectDir, "yarn.lock"))) return "yarn";
|
|
43606
|
+
if (existsSync5(join8(projectDir, "package-lock.json"))) return "npm";
|
|
43607
|
+
return detectPackageManager() ?? "npm";
|
|
43608
|
+
}
|
|
43609
|
+
function hasScript(projectDir, script) {
|
|
43610
|
+
try {
|
|
43611
|
+
const pkg = JSON.parse(
|
|
43612
|
+
readFileSync6(join8(projectDir, "package.json"), "utf-8")
|
|
43613
|
+
);
|
|
43614
|
+
return Boolean(pkg.scripts?.[script]);
|
|
43615
|
+
} catch {
|
|
43616
|
+
return false;
|
|
43617
|
+
}
|
|
43618
|
+
}
|
|
43619
|
+
function runScript(projectDir, script, extraEnv = {}) {
|
|
43620
|
+
const pm = detectProjectPackageManager(projectDir);
|
|
43621
|
+
const result = spawnSync4(pm, ["run", script], {
|
|
43622
|
+
cwd: projectDir,
|
|
43623
|
+
stdio: "inherit",
|
|
43624
|
+
shell: SPAWN_VIA_SHELL3,
|
|
43625
|
+
env: { ...process.env, ...extraEnv }
|
|
43626
|
+
});
|
|
43627
|
+
if (result.error) {
|
|
43628
|
+
throw new Error(
|
|
43629
|
+
`Could not run \`${pm} run ${script}\`: ${result.error.message}`
|
|
43630
|
+
);
|
|
43631
|
+
}
|
|
43632
|
+
if (result.status !== 0) {
|
|
43633
|
+
throw new Error(
|
|
43634
|
+
`\`${pm} run ${script}\` failed with exit code ${result.status ?? "unknown"}`
|
|
43635
|
+
);
|
|
43636
|
+
}
|
|
43637
|
+
}
|
|
43638
|
+
function runInstall(c2, projectDir) {
|
|
43639
|
+
const pm = detectProjectPackageManager(projectDir);
|
|
43640
|
+
const spin = c2.agent ? null : spinner2();
|
|
43641
|
+
spin?.start(`Installing dependencies with ${pm}`);
|
|
43642
|
+
const result = spawnSync4(pm, ["install"], {
|
|
43643
|
+
cwd: projectDir,
|
|
43644
|
+
stdio: ["ignore", "pipe", "pipe"],
|
|
43645
|
+
encoding: "utf-8",
|
|
43646
|
+
shell: SPAWN_VIA_SHELL3
|
|
43647
|
+
});
|
|
43648
|
+
if (result.error || result.status !== 0) {
|
|
43649
|
+
spin?.error(`${pm} install failed`);
|
|
43650
|
+
throw new Error(
|
|
43651
|
+
`\`${pm} install\` failed${result.status != null ? ` with exit code ${result.status}` : ""} \u2014 run it manually inside ${projectDir}, then retry.${outputTail(result)}`
|
|
43652
|
+
);
|
|
43653
|
+
}
|
|
43654
|
+
spin?.stop(`Installed dependencies with ${pm}`);
|
|
43655
|
+
}
|
|
43656
|
+
async function downloadArchive(sourceUrl) {
|
|
43657
|
+
const response = await fetch(sourceUrl);
|
|
43658
|
+
if (!response.ok) {
|
|
43659
|
+
throw new Error(
|
|
43660
|
+
`Download failed: ${response.status} ${response.statusText}`
|
|
43661
|
+
);
|
|
43662
|
+
}
|
|
43663
|
+
return new Uint8Array(await response.arrayBuffer());
|
|
43664
|
+
}
|
|
43665
|
+
|
|
43666
|
+
// src/apps/init.ts
|
|
43667
|
+
var initOptions = external_exports.object({
|
|
43668
|
+
name: external_exports.string().optional().describe("Name for the new app (prompted for interactively if omitted)"),
|
|
43669
|
+
route: external_exports.string().optional().describe(
|
|
43670
|
+
"Route the app is served from (<route>.whop.app). Defaults to a slug of the name"
|
|
43671
|
+
),
|
|
43672
|
+
template: external_exports.string().optional().describe(
|
|
43673
|
+
"Clone this app's published source archive (app_xxx) instead of the blank TanStack Start template"
|
|
43674
|
+
),
|
|
43675
|
+
dir: external_exports.string().optional().describe("Target directory (defaults to ./<route>)")
|
|
43676
|
+
});
|
|
43677
|
+
var initOutput = external_exports.object({
|
|
43678
|
+
app_id: external_exports.string(),
|
|
43679
|
+
name: external_exports.string(),
|
|
43680
|
+
route: external_exports.string(),
|
|
43681
|
+
dir: external_exports.string(),
|
|
43682
|
+
manual_steps: external_exports.array(external_exports.string()).optional()
|
|
43683
|
+
});
|
|
43684
|
+
async function resolveNameAndRoute(c2, options) {
|
|
43685
|
+
if (options.name) {
|
|
43686
|
+
const route = slugify3(options.route ?? options.name);
|
|
43687
|
+
if (!route) {
|
|
43688
|
+
throw new CommandAbort({
|
|
43689
|
+
code: "ROUTE_REQUIRED",
|
|
43690
|
+
message: `Couldn't derive a route from "${options.route ?? options.name}" \u2014 use letters and numbers (e.g. my-site).`,
|
|
43691
|
+
retryable: true
|
|
43692
|
+
});
|
|
43693
|
+
}
|
|
43694
|
+
return { name: options.name, route };
|
|
43695
|
+
}
|
|
43696
|
+
if (c2.agent) {
|
|
43697
|
+
throw new CommandAbort({
|
|
43698
|
+
code: "NAME_REQUIRED",
|
|
43699
|
+
message: 'Ask the user what the app is called and which route it should live at (the route becomes <route>.whop.app), then re-run with flags:\n whop apps init --name "My Site" --route my-site',
|
|
43700
|
+
retryable: true,
|
|
43701
|
+
cta: {
|
|
43702
|
+
commands: [
|
|
43703
|
+
{
|
|
43704
|
+
command: "apps init",
|
|
43705
|
+
description: "Re-run with --name (and optionally --route)"
|
|
43706
|
+
}
|
|
43707
|
+
]
|
|
43708
|
+
}
|
|
43709
|
+
});
|
|
43710
|
+
}
|
|
43711
|
+
if (options.route) {
|
|
43712
|
+
const name = await promptText("What is your app called?", "My Site");
|
|
43713
|
+
if (name === null) cancelled();
|
|
43714
|
+
const route = slugify3(options.route);
|
|
43715
|
+
if (!route) {
|
|
43716
|
+
throw new CommandAbort({
|
|
43717
|
+
code: "ROUTE_REQUIRED",
|
|
43718
|
+
message: `Couldn't derive a route from "${options.route}" \u2014 use letters and numbers (e.g. my-site).`,
|
|
43719
|
+
retryable: true
|
|
43720
|
+
});
|
|
43721
|
+
}
|
|
43722
|
+
return { name, route };
|
|
43723
|
+
}
|
|
43724
|
+
return promptForNameAndRoute();
|
|
43725
|
+
}
|
|
43726
|
+
function ensureEmptyTarget(targetDir) {
|
|
43727
|
+
if (existsSync6(targetDir) && readdirSync3(targetDir).length > 0) {
|
|
43728
|
+
throw new CommandAbort({
|
|
43729
|
+
code: "TARGET_NOT_EMPTY",
|
|
43730
|
+
message: `Target directory is not empty: ${targetDir}. Pick a different route or pass --dir <empty-dir>. To work on an existing app's code, use \`whop apps pull\`.`
|
|
43731
|
+
});
|
|
43732
|
+
}
|
|
43733
|
+
}
|
|
43734
|
+
async function fetchTemplateSource(templateId) {
|
|
43735
|
+
let template;
|
|
43736
|
+
try {
|
|
43737
|
+
template = await getApp(templateId);
|
|
43738
|
+
} catch (err2) {
|
|
43739
|
+
throw new CommandAbort({
|
|
43740
|
+
code: "TEMPLATE_NOT_FOUND",
|
|
43741
|
+
message: `Could not load template app ${templateId}: ${err2 instanceof Error ? err2.message : "unknown error"}`,
|
|
43742
|
+
retryable: true
|
|
43743
|
+
});
|
|
43744
|
+
}
|
|
43745
|
+
const sourceUrl = template.production_web_build?.source_url;
|
|
43746
|
+
if (!sourceUrl) {
|
|
43747
|
+
throw new CommandAbort({
|
|
43748
|
+
code: "TEMPLATE_HAS_NO_SOURCE",
|
|
43749
|
+
message: `${template.name} (${template.id}) has no published source archive, so it can't be used as a template.`
|
|
43750
|
+
});
|
|
43751
|
+
}
|
|
43752
|
+
return { template, sourceUrl };
|
|
43753
|
+
}
|
|
43754
|
+
async function runInitFlow(c2, options, pm = ensurePackageManager()) {
|
|
43755
|
+
const templateSource = options.template ? await fetchTemplateSource(options.template) : null;
|
|
43756
|
+
const { name, route } = await resolveNameAndRoute(c2, options);
|
|
43757
|
+
const targetDir = resolve6(options.dir ?? `./${route}`);
|
|
43758
|
+
ensureEmptyTarget(targetDir);
|
|
43759
|
+
let app;
|
|
43760
|
+
try {
|
|
43761
|
+
app = await createApp({ name, route });
|
|
43762
|
+
} catch (err2) {
|
|
43763
|
+
throw new CommandAbort({
|
|
43764
|
+
code: "CREATE_FAILED",
|
|
43765
|
+
message: err2 instanceof Error ? err2.message : "Failed to register the app",
|
|
43766
|
+
retryable: true
|
|
43767
|
+
});
|
|
43768
|
+
}
|
|
43769
|
+
const config = { app_id: app.id, name, route };
|
|
43770
|
+
const spin = c2.agent ? null : spinner3();
|
|
43771
|
+
if (templateSource) {
|
|
43772
|
+
spin?.start(
|
|
43773
|
+
`Cloning ${templateSource.template.name} (${templateSource.template.id}) source archive`
|
|
43774
|
+
);
|
|
43775
|
+
try {
|
|
43776
|
+
const zip = await downloadArchive(templateSource.sourceUrl);
|
|
43777
|
+
const fileCount = extractSourceArchive(zip, targetDir);
|
|
43778
|
+
spin?.stop(
|
|
43779
|
+
`Cloned ${fileCount} files from ${templateSource.template.name} \u2192 ${targetDir}`
|
|
43780
|
+
);
|
|
43781
|
+
} catch (err2) {
|
|
43782
|
+
spin?.error("Template clone failed");
|
|
43783
|
+
throw new CommandAbort({
|
|
43784
|
+
code: "TEMPLATE_CLONE_FAILED",
|
|
43785
|
+
message: `${err2 instanceof Error ? err2.message : "Failed to clone the template source"}
|
|
43786
|
+
|
|
43787
|
+
The app ${app.id} is already registered \u2014 re-run \`whop apps init --template ${options.template} --dir ${targetDir}\` to retry.`,
|
|
43788
|
+
retryable: true
|
|
43789
|
+
});
|
|
43790
|
+
}
|
|
43791
|
+
} else {
|
|
43792
|
+
spin?.start("Scaffolding the latest TanStack Start template");
|
|
43793
|
+
try {
|
|
43794
|
+
runTanstackCreate({ targetDir, projectName: route, pm });
|
|
43795
|
+
spin?.stop(`Scaffolded ${route} from the latest TanStack Start template`);
|
|
43796
|
+
} catch (err2) {
|
|
43797
|
+
spin?.error("Scaffold failed");
|
|
43798
|
+
throw new CommandAbort({
|
|
43799
|
+
code: "SCAFFOLD_FAILED",
|
|
43800
|
+
message: `${err2 instanceof Error ? err2.message : "Failed to scaffold the project"}
|
|
43801
|
+
|
|
43802
|
+
The app ${app.id} is already registered \u2014 fix the problem and re-run \`whop apps init --name "${name}" --route ${route}\`.`
|
|
43803
|
+
});
|
|
43804
|
+
}
|
|
43805
|
+
}
|
|
43806
|
+
writeAppConfig(targetDir, config);
|
|
43807
|
+
const { manualSteps } = whopifyProject({ targetDir, route });
|
|
43808
|
+
try {
|
|
43809
|
+
runInstall(c2, targetDir);
|
|
43810
|
+
} catch (err2) {
|
|
43811
|
+
throw new CommandAbort({
|
|
43812
|
+
code: "INSTALL_FAILED",
|
|
43813
|
+
message: err2 instanceof Error ? err2.message : "Install failed",
|
|
43814
|
+
retryable: true
|
|
43815
|
+
});
|
|
43816
|
+
}
|
|
43817
|
+
initGitRepo(c2, targetDir);
|
|
43818
|
+
return { projectDir: targetDir, config, manualSteps };
|
|
43819
|
+
}
|
|
43820
|
+
function initGitRepo(c2, targetDir) {
|
|
43821
|
+
if (!gitInstalled()) {
|
|
43822
|
+
log2(
|
|
43823
|
+
c2,
|
|
43824
|
+
chalk.yellow("git is not installed \u2014 skipping repository setup.")
|
|
43825
|
+
);
|
|
43826
|
+
return;
|
|
43827
|
+
}
|
|
43828
|
+
if (repoToplevel(targetDir)) return;
|
|
43829
|
+
try {
|
|
43830
|
+
gitInit(targetDir);
|
|
43831
|
+
commitAll(targetDir, "Initial commit from whop apps init");
|
|
43832
|
+
} catch (err2) {
|
|
43833
|
+
log2(
|
|
43834
|
+
c2,
|
|
43835
|
+
chalk.yellow(
|
|
43836
|
+
`Couldn't create the git repository (${err2 instanceof Error ? err2.message : "unknown error"}) \u2014 continuing without one.`
|
|
43837
|
+
)
|
|
43838
|
+
);
|
|
43839
|
+
}
|
|
43840
|
+
}
|
|
43841
|
+
|
|
43842
|
+
// src/apps/pull.ts
|
|
43843
|
+
import { mkdtempSync as mkdtempSync2, mkdirSync as mkdirSync4, rmSync as rmSync3 } from "fs";
|
|
43844
|
+
import { tmpdir as tmpdir2 } from "os";
|
|
43845
|
+
import { join as join9, resolve as resolve7 } from "path";
|
|
43846
|
+
var pullOptions = external_exports.object({
|
|
43847
|
+
app: external_exports.string().optional().describe("App id (defaults to the project's linked app)"),
|
|
43848
|
+
build: external_exports.string().optional().describe(
|
|
43849
|
+
"Pull this specific build's source (abld_xxx) instead of the current production build"
|
|
43850
|
+
),
|
|
43851
|
+
dir: external_exports.string().optional().describe(
|
|
43852
|
+
"Target directory (defaults to the linked project, or ./<route> on a fresh machine)"
|
|
43853
|
+
),
|
|
43854
|
+
overwrite: external_exports.boolean().optional().describe(
|
|
43855
|
+
"Skip the merge and write the archive files straight over the working tree"
|
|
43856
|
+
)
|
|
43857
|
+
});
|
|
43858
|
+
var pullOutput = external_exports.object({
|
|
43859
|
+
app_id: external_exports.string(),
|
|
43860
|
+
build_id: external_exports.string().optional(),
|
|
43861
|
+
dir: external_exports.string(),
|
|
43862
|
+
status: external_exports.enum(["merged", "up_to_date", "overwritten"]),
|
|
43863
|
+
autostash: external_exports.enum(["popped", "kept"]).optional()
|
|
43864
|
+
});
|
|
43865
|
+
function requireGitInstalled() {
|
|
43866
|
+
if (gitInstalled()) return;
|
|
43867
|
+
throw new CommandAbort({
|
|
43868
|
+
code: "GIT_REQUIRED",
|
|
43869
|
+
message: "`whop apps pull` needs git to merge the remote source safely, and git is not installed. Install it from https://git-scm.com/downloads and re-run."
|
|
43870
|
+
});
|
|
43871
|
+
}
|
|
43872
|
+
async function resolveSource(options) {
|
|
43873
|
+
if (options.build) {
|
|
43874
|
+
let build;
|
|
43875
|
+
try {
|
|
43876
|
+
build = await getAppBuild(options.build);
|
|
43877
|
+
} catch (err2) {
|
|
43878
|
+
throw new CommandAbort({
|
|
43879
|
+
code: "BUILD_NOT_FOUND",
|
|
43880
|
+
message: `Could not load build ${options.build}: ${err2 instanceof Error ? err2.message : "unknown error"}`,
|
|
43881
|
+
retryable: true
|
|
43882
|
+
});
|
|
43883
|
+
}
|
|
43884
|
+
if (!build.source_url) {
|
|
43885
|
+
throw new CommandAbort({
|
|
43886
|
+
code: "BUILD_HAS_NO_SOURCE",
|
|
43887
|
+
message: `Build ${build.id} has no source archive (it was uploaded without one).`
|
|
43888
|
+
});
|
|
43889
|
+
}
|
|
43890
|
+
const appId2 = options.app ?? linkedAppId();
|
|
43891
|
+
if (!appId2) {
|
|
43892
|
+
throw new CommandAbort({
|
|
43893
|
+
code: "APP_REQUIRED",
|
|
43894
|
+
message: `Builds don't carry their app id \u2014 pass --app app_xxx alongside --build, or run inside a project with ${APP_CONFIG_FILENAME}.`,
|
|
43895
|
+
retryable: true
|
|
43896
|
+
});
|
|
43897
|
+
}
|
|
43898
|
+
return {
|
|
43899
|
+
app: await loadApp(appId2),
|
|
43900
|
+
buildId: build.id,
|
|
43901
|
+
sourceUrl: build.source_url
|
|
43902
|
+
};
|
|
43903
|
+
}
|
|
43904
|
+
const appId = options.app ?? linkedAppId();
|
|
43905
|
+
if (!appId) {
|
|
43906
|
+
throw new CommandAbort({
|
|
43907
|
+
code: "APP_REQUIRED",
|
|
43908
|
+
message: `No app specified. Pass --app app_xxx, or run inside a project with ${APP_CONFIG_FILENAME}. Use \`whop apps list\` to find the app id.`,
|
|
43909
|
+
retryable: true,
|
|
43910
|
+
cta: {
|
|
43911
|
+
commands: [
|
|
43912
|
+
{ command: "apps list", description: "Find the app id" },
|
|
43913
|
+
{
|
|
43914
|
+
command: "apps pull",
|
|
43915
|
+
description: "Re-run with --app <app_id>"
|
|
43916
|
+
}
|
|
43917
|
+
]
|
|
43918
|
+
}
|
|
43919
|
+
});
|
|
43920
|
+
}
|
|
43921
|
+
const app = await loadApp(appId);
|
|
43922
|
+
const production = app.production_web_build;
|
|
43923
|
+
if (!production?.source_url) {
|
|
43924
|
+
throw new CommandAbort({
|
|
43925
|
+
code: "APP_HAS_NO_SOURCE",
|
|
43926
|
+
message: `${app.name} (${app.id}) has no production build with a source archive to pull. Deploy from the machine that has the code, or pass --build abld_xxx for a specific build that has one.`
|
|
43927
|
+
});
|
|
43928
|
+
}
|
|
43929
|
+
return { app, buildId: production.id, sourceUrl: production.source_url };
|
|
43930
|
+
}
|
|
43931
|
+
function linkedAppId() {
|
|
43932
|
+
const projectDir = findProjectDir();
|
|
43933
|
+
const config = projectDir ? readAppConfig(projectDir) : null;
|
|
43934
|
+
return config?.app_id ?? null;
|
|
43935
|
+
}
|
|
43936
|
+
async function loadApp(appId) {
|
|
43937
|
+
try {
|
|
43938
|
+
return await getApp(appId);
|
|
43939
|
+
} catch (err2) {
|
|
43940
|
+
throw new CommandAbort({
|
|
43941
|
+
code: "APP_NOT_FOUND",
|
|
43942
|
+
message: `Could not load app ${appId}: ${err2 instanceof Error ? err2.message : "unknown error"}`,
|
|
43943
|
+
retryable: true
|
|
43944
|
+
});
|
|
43945
|
+
}
|
|
43946
|
+
}
|
|
43947
|
+
function resolveTargetDir(options, app) {
|
|
43948
|
+
if (options.dir) return resolve7(options.dir);
|
|
43949
|
+
const projectDir = findProjectDir();
|
|
43950
|
+
if (projectDir && readAppConfig(projectDir)?.app_id === app.id) {
|
|
43951
|
+
return projectDir;
|
|
43952
|
+
}
|
|
43953
|
+
const route = app.route;
|
|
43954
|
+
if (!route) {
|
|
43955
|
+
throw new CommandAbort({
|
|
43956
|
+
code: "DIR_REQUIRED",
|
|
43957
|
+
message: `${app.name} (${app.id}) has no route to name a directory after \u2014 pass --dir <path>.`,
|
|
43958
|
+
retryable: true
|
|
43959
|
+
});
|
|
43960
|
+
}
|
|
43961
|
+
return resolve7(`./${route}`);
|
|
43962
|
+
}
|
|
43963
|
+
function ensureUsableRepo(targetDir) {
|
|
43964
|
+
const status = repoStatus(targetDir);
|
|
43965
|
+
if (status.kind === "none") {
|
|
43966
|
+
gitInit(targetDir);
|
|
43967
|
+
if (isDirty(targetDir)) {
|
|
43968
|
+
commitAll(targetDir, "Local files before whop apps pull");
|
|
43969
|
+
}
|
|
43970
|
+
return;
|
|
43971
|
+
}
|
|
43972
|
+
if (status.kind === "nested") {
|
|
43973
|
+
throw new CommandAbort({
|
|
43974
|
+
code: "NESTED_IN_REPO",
|
|
43975
|
+
message: `${targetDir} is inside the git repository at ${status.toplevel}. Pull merges at the repository root only \u2014 run it in a standalone directory, or pass --overwrite to just write the files.`
|
|
43976
|
+
});
|
|
43977
|
+
}
|
|
43978
|
+
}
|
|
43979
|
+
async function runPullFlow(options) {
|
|
43980
|
+
requireGitInstalled();
|
|
43981
|
+
const source = await resolveSource(options);
|
|
43982
|
+
const { app } = source;
|
|
43983
|
+
const targetDir = resolveTargetDir(options, app);
|
|
43984
|
+
mkdirSync4(targetDir, { recursive: true });
|
|
43985
|
+
const zip = await downloadArchive(source.sourceUrl);
|
|
43986
|
+
const linkConfig = {
|
|
43987
|
+
app_id: app.id,
|
|
43988
|
+
name: app.name,
|
|
43989
|
+
route: app.route ?? ""
|
|
43990
|
+
};
|
|
43991
|
+
if (options.overwrite) {
|
|
43992
|
+
extractSourceArchive(zip, targetDir);
|
|
43993
|
+
writeAppConfig(targetDir, linkConfig);
|
|
43994
|
+
recordOverwriteSnapshot(zip, targetDir, app.id, source.buildId);
|
|
43995
|
+
return {
|
|
43996
|
+
app_id: app.id,
|
|
43997
|
+
build_id: source.buildId,
|
|
43998
|
+
dir: targetDir,
|
|
43999
|
+
status: "overwritten",
|
|
44000
|
+
autostash: void 0
|
|
44001
|
+
};
|
|
44002
|
+
}
|
|
44003
|
+
const snapshotDir = mkdtempSync2(join9(tmpdir2(), "whop-pull-"));
|
|
44004
|
+
try {
|
|
44005
|
+
extractSourceArchive(zip, snapshotDir);
|
|
44006
|
+
ensureUsableRepo(targetDir);
|
|
44007
|
+
const message = `whop apps pull: ${app.id}${source.buildId ? ` @ ${source.buildId}` : ""}`;
|
|
44008
|
+
const snapshot = commitSnapshot(targetDir, snapshotDir, app.id, message);
|
|
44009
|
+
if (hasCommits(targetDir) && (isMerged(targetDir, snapshot.commit) || nothingToMerge(targetDir, snapshot.commit))) {
|
|
44010
|
+
ensureLinked(targetDir, linkConfig);
|
|
44011
|
+
return {
|
|
44012
|
+
app_id: app.id,
|
|
44013
|
+
build_id: source.buildId,
|
|
44014
|
+
dir: targetDir,
|
|
44015
|
+
status: "up_to_date",
|
|
44016
|
+
autostash: void 0
|
|
44017
|
+
};
|
|
44018
|
+
}
|
|
44019
|
+
if (!hasCommits(targetDir)) {
|
|
44020
|
+
resetHard(targetDir, snapshot.commit);
|
|
44021
|
+
ensureLinked(targetDir, linkConfig);
|
|
44022
|
+
return {
|
|
44023
|
+
app_id: app.id,
|
|
44024
|
+
build_id: source.buildId,
|
|
44025
|
+
dir: targetDir,
|
|
44026
|
+
status: "merged",
|
|
44027
|
+
autostash: void 0
|
|
44028
|
+
};
|
|
44029
|
+
}
|
|
44030
|
+
const stashed = isDirty(targetDir) && stashPush(targetDir, "whop apps pull autostash") === "stashed";
|
|
44031
|
+
const merge2 = mergeSnapshot(targetDir, snapshot.commit, message);
|
|
44032
|
+
if (merge2.status === "conflicts") {
|
|
44033
|
+
throw new CommandAbort({
|
|
44034
|
+
code: "PULL_CONFLICTS",
|
|
44035
|
+
message: [
|
|
44036
|
+
`Merge conflicts in ${targetDir} \u2014 the remote source and your local code changed the same files:`,
|
|
44037
|
+
...merge2.files.map((file) => ` ${file}`),
|
|
44038
|
+
"",
|
|
44039
|
+
"Resolve the conflict markers, then `git add -A && git commit` to finish the merge. Nothing was lost.",
|
|
44040
|
+
...stashed ? [
|
|
44041
|
+
"Your uncommitted changes are safe in the autostash \u2014 `git stash pop` after committing the merge."
|
|
44042
|
+
] : []
|
|
44043
|
+
].join("\n")
|
|
44044
|
+
});
|
|
44045
|
+
}
|
|
44046
|
+
const autostash = stashed ? stashPop(targetDir) ? "popped" : "kept" : void 0;
|
|
44047
|
+
ensureLinked(targetDir, linkConfig);
|
|
44048
|
+
return {
|
|
44049
|
+
app_id: app.id,
|
|
44050
|
+
build_id: source.buildId,
|
|
44051
|
+
dir: targetDir,
|
|
44052
|
+
status: "merged",
|
|
44053
|
+
autostash
|
|
44054
|
+
};
|
|
44055
|
+
} finally {
|
|
44056
|
+
rmSync3(snapshotDir, { recursive: true, force: true });
|
|
44057
|
+
}
|
|
44058
|
+
}
|
|
44059
|
+
function recordOverwriteSnapshot(zip, targetDir, appId, buildId) {
|
|
44060
|
+
if (!gitInstalled() || repoStatus(targetDir).kind !== "root") return;
|
|
44061
|
+
const snapshotDir = mkdtempSync2(join9(tmpdir2(), "whop-pull-"));
|
|
44062
|
+
try {
|
|
44063
|
+
extractSourceArchive(zip, snapshotDir);
|
|
44064
|
+
commitSnapshot(
|
|
44065
|
+
targetDir,
|
|
44066
|
+
snapshotDir,
|
|
44067
|
+
appId,
|
|
44068
|
+
`whop apps pull --overwrite: ${appId}${buildId ? ` @ ${buildId}` : ""}`
|
|
44069
|
+
);
|
|
44070
|
+
} finally {
|
|
44071
|
+
rmSync3(snapshotDir, { recursive: true, force: true });
|
|
44072
|
+
}
|
|
44073
|
+
}
|
|
44074
|
+
function ensureLinked(targetDir, config) {
|
|
44075
|
+
if (readAppConfig(targetDir)?.app_id === config.app_id) return;
|
|
44076
|
+
writeAppConfig(targetDir, config);
|
|
44077
|
+
}
|
|
44078
|
+
|
|
43227
44079
|
// src/apps/commands.ts
|
|
43228
44080
|
var BUILD_ARCHIVE = "dist/whop-build.zip";
|
|
43229
44081
|
var UNSAFE_DEV_ENV_KEYS = /* @__PURE__ */ new Set([
|
|
@@ -43250,83 +44102,6 @@ var BuildSchema = external_exports.object({
|
|
|
43250
44102
|
is_production: external_exports.boolean().optional(),
|
|
43251
44103
|
url: external_exports.string().optional()
|
|
43252
44104
|
});
|
|
43253
|
-
var DeployAbort = class extends Error {
|
|
43254
|
-
constructor(options) {
|
|
43255
|
-
super(options.message);
|
|
43256
|
-
this.options = options;
|
|
43257
|
-
}
|
|
43258
|
-
};
|
|
43259
|
-
function detectProjectPackageManager(projectDir) {
|
|
43260
|
-
if (existsSync5(join7(projectDir, "bun.lock")) || existsSync5(join7(projectDir, "bun.lockb")))
|
|
43261
|
-
return "bun";
|
|
43262
|
-
if (existsSync5(join7(projectDir, "pnpm-lock.yaml"))) return "pnpm";
|
|
43263
|
-
if (existsSync5(join7(projectDir, "yarn.lock"))) return "yarn";
|
|
43264
|
-
if (existsSync5(join7(projectDir, "package-lock.json"))) return "npm";
|
|
43265
|
-
return detectPackageManager() ?? "npm";
|
|
43266
|
-
}
|
|
43267
|
-
function hasScript(projectDir, script) {
|
|
43268
|
-
try {
|
|
43269
|
-
const pkg = JSON.parse(
|
|
43270
|
-
readFileSync6(join7(projectDir, "package.json"), "utf-8")
|
|
43271
|
-
);
|
|
43272
|
-
return Boolean(pkg.scripts?.[script]);
|
|
43273
|
-
} catch {
|
|
43274
|
-
return false;
|
|
43275
|
-
}
|
|
43276
|
-
}
|
|
43277
|
-
var SPAWN_VIA_SHELL2 = process.platform === "win32";
|
|
43278
|
-
function runScript(projectDir, script, extraEnv = {}) {
|
|
43279
|
-
const pm = detectProjectPackageManager(projectDir);
|
|
43280
|
-
const result = spawnSync3(pm, ["run", script], {
|
|
43281
|
-
cwd: projectDir,
|
|
43282
|
-
stdio: "inherit",
|
|
43283
|
-
shell: SPAWN_VIA_SHELL2,
|
|
43284
|
-
env: { ...process.env, ...extraEnv }
|
|
43285
|
-
});
|
|
43286
|
-
if (result.error) {
|
|
43287
|
-
throw new Error(
|
|
43288
|
-
`Could not run \`${pm} run ${script}\`: ${result.error.message}`
|
|
43289
|
-
);
|
|
43290
|
-
}
|
|
43291
|
-
if (result.status !== 0) {
|
|
43292
|
-
throw new Error(
|
|
43293
|
-
`\`${pm} run ${script}\` failed with exit code ${result.status ?? "unknown"}`
|
|
43294
|
-
);
|
|
43295
|
-
}
|
|
43296
|
-
}
|
|
43297
|
-
function runInstall(c2, projectDir) {
|
|
43298
|
-
const pm = detectProjectPackageManager(projectDir);
|
|
43299
|
-
const spin = c2.agent ? null : spinner2();
|
|
43300
|
-
spin?.start(`Installing dependencies with ${pm}`);
|
|
43301
|
-
const result = spawnSync3(pm, ["install"], {
|
|
43302
|
-
cwd: projectDir,
|
|
43303
|
-
stdio: ["ignore", "pipe", "pipe"],
|
|
43304
|
-
encoding: "utf-8",
|
|
43305
|
-
shell: SPAWN_VIA_SHELL2
|
|
43306
|
-
});
|
|
43307
|
-
if (result.error || result.status !== 0) {
|
|
43308
|
-
spin?.error(`${pm} install failed`);
|
|
43309
|
-
throw new Error(
|
|
43310
|
-
`\`${pm} install\` failed${result.status != null ? ` with exit code ${result.status}` : ""} \u2014 run it manually inside ${projectDir}, then re-run \`whop apps deploy\`.${outputTail(result)}`
|
|
43311
|
-
);
|
|
43312
|
-
}
|
|
43313
|
-
spin?.stop(`Installed dependencies with ${pm}`);
|
|
43314
|
-
}
|
|
43315
|
-
function log2(c2, message) {
|
|
43316
|
-
if (!c2.agent) console.log(message);
|
|
43317
|
-
}
|
|
43318
|
-
async function promptText(message, placeholder, initialValue) {
|
|
43319
|
-
const value = await text2({ message, placeholder, initialValue });
|
|
43320
|
-
if (isCancel3(value)) return null;
|
|
43321
|
-
return String(value);
|
|
43322
|
-
}
|
|
43323
|
-
function cancelled() {
|
|
43324
|
-
throw new DeployAbort({
|
|
43325
|
-
code: "CANCELLED",
|
|
43326
|
-
message: "Cancelled.",
|
|
43327
|
-
exitCode: 130
|
|
43328
|
-
});
|
|
43329
|
-
}
|
|
43330
44105
|
function formatManualSteps(steps) {
|
|
43331
44106
|
return steps.map((step, i) => ` ${i + 1}. ${step}`).join("\n");
|
|
43332
44107
|
}
|
|
@@ -43347,117 +44122,46 @@ async function registerSpecCommands(group, tag) {
|
|
|
43347
44122
|
if ("run" in entry) group.command(name, entry);
|
|
43348
44123
|
}
|
|
43349
44124
|
}
|
|
43350
|
-
function
|
|
43351
|
-
const
|
|
43352
|
-
|
|
43353
|
-
|
|
43354
|
-
code: "NO_PACKAGE_MANAGER",
|
|
43355
|
-
message: `No package manager found (looked for bun, pnpm, npm). Install one and re-run \`whop apps deploy\`:
|
|
43356
|
-
|
|
43357
|
-
bun (recommended): ${pmInstallHint("bun")}
|
|
43358
|
-
pnpm: ${pmInstallHint("pnpm")}
|
|
43359
|
-
npm: ${pmInstallHint("npm")}`
|
|
43360
|
-
});
|
|
43361
|
-
}
|
|
43362
|
-
async function ensureViteApp(c2, pm, dirOption) {
|
|
43363
|
-
const projectDir = dirOption ? resolve6(dirOption) : findProjectDir() ?? process.cwd();
|
|
43364
|
-
const isViteApp = existsSync5(join7(projectDir, "package.json")) && findViteConfig(projectDir) !== void 0;
|
|
43365
|
-
if (isViteApp) return { projectDir, freshlyScaffolded: false };
|
|
44125
|
+
async function resolveDeployProject(c2, dirOption) {
|
|
44126
|
+
const projectDir = dirOption ? resolve8(dirOption) : findProjectDir() ?? process.cwd();
|
|
44127
|
+
const isViteApp = existsSync7(join10(projectDir, "package.json")) && findViteConfig(projectDir) !== void 0;
|
|
44128
|
+
if (isViteApp) return { projectDir, freshInit: null };
|
|
43366
44129
|
if (c2.agent) {
|
|
43367
|
-
throw new
|
|
44130
|
+
throw new CommandAbort({
|
|
43368
44131
|
code: "NOT_A_VITE_APP",
|
|
43369
44132
|
message: [
|
|
43370
|
-
`${projectDir} doesn't look like a Vite app (missing package.json or vite config).`,
|
|
44133
|
+
`${projectDir} doesn't look like a Vite app (missing package.json or vite config), and deploy only ships existing projects.`,
|
|
43371
44134
|
"",
|
|
43372
|
-
"
|
|
44135
|
+
"To start a new app here (asks nothing else \u2014 scaffolds, registers, and links):",
|
|
44136
|
+
' whop apps init --name "My Site" --route my-site',
|
|
43373
44137
|
"",
|
|
43374
|
-
"To
|
|
43375
|
-
|
|
43376
|
-
` cd <app-name> && ${pm} install`,
|
|
44138
|
+
"To get an existing app's deployed source onto this machine:",
|
|
44139
|
+
" whop apps pull --app app_xxxxxxxx",
|
|
43377
44140
|
"",
|
|
43378
|
-
"Then re-run `whop apps deploy` from inside the
|
|
43379
|
-
"Or, if a Vite app already exists somewhere else, re-run `whop apps deploy --dir <path-to-app>`."
|
|
44141
|
+
"Then re-run `whop apps deploy` from inside the project directory."
|
|
43380
44142
|
].join("\n"),
|
|
43381
44143
|
retryable: true,
|
|
43382
44144
|
cta: {
|
|
43383
44145
|
commands: [
|
|
44146
|
+
{ command: "apps init", description: "Scaffold a new Whop app" },
|
|
43384
44147
|
{
|
|
43385
|
-
command: "apps
|
|
43386
|
-
description: "
|
|
44148
|
+
command: "apps pull",
|
|
44149
|
+
description: "Fetch an existing app's source"
|
|
43387
44150
|
}
|
|
43388
44151
|
]
|
|
43389
44152
|
}
|
|
43390
44153
|
});
|
|
43391
44154
|
}
|
|
43392
|
-
const choice = await
|
|
43393
|
-
|
|
43394
|
-
|
|
43395
|
-
{
|
|
43396
|
-
value: "scaffold",
|
|
43397
|
-
label: "Scaffold a new Whop app here (TanStack Start)"
|
|
43398
|
-
},
|
|
44155
|
+
const choice = await promptSelect(
|
|
44156
|
+
`${projectDir} doesn't look like a Vite app. What do you want to do?`,
|
|
44157
|
+
[
|
|
44158
|
+
{ value: "init", label: "Create a new Whop app (runs whop apps init)" },
|
|
43399
44159
|
{ value: "cancel", label: "Cancel \u2014 I'll cd into my app directory" }
|
|
43400
44160
|
]
|
|
43401
|
-
});
|
|
43402
|
-
if (isCancel3(choice) || choice === "cancel") cancelled();
|
|
43403
|
-
const scaffolded = await scaffoldNewApp(c2, pm);
|
|
43404
|
-
return { projectDir: scaffolded, freshlyScaffolded: true };
|
|
43405
|
-
}
|
|
43406
|
-
async function promptForNameAndRoute() {
|
|
43407
|
-
const name = await promptText("What is your app called?", "My Site");
|
|
43408
|
-
if (name === null) cancelled();
|
|
43409
|
-
const slug = slugify3(name);
|
|
43410
|
-
const routeInput = await promptText(
|
|
43411
|
-
"Which route should it live at? (your-route.whop.app)",
|
|
43412
|
-
slug,
|
|
43413
|
-
slug
|
|
43414
44161
|
);
|
|
43415
|
-
if (
|
|
43416
|
-
const
|
|
43417
|
-
|
|
43418
|
-
throw new DeployAbort({
|
|
43419
|
-
code: "ROUTE_REQUIRED",
|
|
43420
|
-
message: `Couldn't derive a route from "${routeInput}" \u2014 use letters and numbers (e.g. my-site).`,
|
|
43421
|
-
retryable: true
|
|
43422
|
-
});
|
|
43423
|
-
}
|
|
43424
|
-
return { name, route };
|
|
43425
|
-
}
|
|
43426
|
-
async function scaffoldNewApp(c2, pm) {
|
|
43427
|
-
const { name, route } = await promptForNameAndRoute();
|
|
43428
|
-
const targetDir = resolve6(`./${route}`);
|
|
43429
|
-
if (existsSync5(targetDir) && readdirSync3(targetDir).length > 0) {
|
|
43430
|
-
throw new DeployAbort({
|
|
43431
|
-
code: "TARGET_NOT_EMPTY",
|
|
43432
|
-
message: `Target directory is not empty: ${targetDir}. Pick a different route, or run \`whop apps deploy\` from an empty directory.`
|
|
43433
|
-
});
|
|
43434
|
-
}
|
|
43435
|
-
let app;
|
|
43436
|
-
try {
|
|
43437
|
-
app = await createApp({ name, route });
|
|
43438
|
-
} catch (err2) {
|
|
43439
|
-
throw new DeployAbort({
|
|
43440
|
-
code: "CREATE_FAILED",
|
|
43441
|
-
message: err2 instanceof Error ? err2.message : "Failed to register the app",
|
|
43442
|
-
retryable: true
|
|
43443
|
-
});
|
|
43444
|
-
}
|
|
43445
|
-
const spin = c2.agent ? null : spinner2();
|
|
43446
|
-
spin?.start("Scaffolding the latest TanStack Start template");
|
|
43447
|
-
try {
|
|
43448
|
-
runTanstackCreate({ targetDir, projectName: route, pm });
|
|
43449
|
-
spin?.stop(`Scaffolded ${route} from the latest TanStack Start template`);
|
|
43450
|
-
} catch (err2) {
|
|
43451
|
-
spin?.error("Scaffold failed");
|
|
43452
|
-
throw new DeployAbort({
|
|
43453
|
-
code: "SCAFFOLD_FAILED",
|
|
43454
|
-
message: `${err2 instanceof Error ? err2.message : "Failed to scaffold the project"}
|
|
43455
|
-
|
|
43456
|
-
The app ${app.id} is already registered \u2014 scaffold into a fresh directory and re-run \`whop apps deploy --app ${app.id}\` there.`
|
|
43457
|
-
});
|
|
43458
|
-
}
|
|
43459
|
-
writeAppConfig(targetDir, { app_id: app.id, name, route });
|
|
43460
|
-
return targetDir;
|
|
44162
|
+
if (choice === "cancel") cancelled();
|
|
44163
|
+
const scaffolded = await runInitFlow(c2, {});
|
|
44164
|
+
return { projectDir: scaffolded.projectDir, freshInit: scaffolded.config };
|
|
43461
44165
|
}
|
|
43462
44166
|
async function ensureAppLinked(c2, projectDir, appOption) {
|
|
43463
44167
|
if (appOption) {
|
|
@@ -43465,7 +44169,7 @@ async function ensureAppLinked(c2, projectDir, appOption) {
|
|
|
43465
44169
|
try {
|
|
43466
44170
|
app = await getApp(appOption);
|
|
43467
44171
|
} catch (err2) {
|
|
43468
|
-
throw new
|
|
44172
|
+
throw new CommandAbort({
|
|
43469
44173
|
code: "APP_NOT_FOUND",
|
|
43470
44174
|
message: `Could not load app ${appOption}: ${err2 instanceof Error ? err2.message : "unknown error"}`,
|
|
43471
44175
|
retryable: true
|
|
@@ -43475,7 +44179,7 @@ async function ensureAppLinked(c2, projectDir, appOption) {
|
|
|
43475
44179
|
writeAppConfig(projectDir, config2);
|
|
43476
44180
|
log2(
|
|
43477
44181
|
c2,
|
|
43478
|
-
|
|
44182
|
+
chalk2.dim(
|
|
43479
44183
|
`Linked ${config2.name} (${config2.app_id}) \u2192 ${APP_CONFIG_FILENAME}`
|
|
43480
44184
|
)
|
|
43481
44185
|
);
|
|
@@ -43491,12 +44195,12 @@ ${formatAppList(apps)}
|
|
|
43491
44195
|
To reuse one: whop apps deploy --app <app_id>
|
|
43492
44196
|
|
|
43493
44197
|
` : "This account has no apps yet, so a new one must be created.\n\n";
|
|
43494
|
-
throw new
|
|
44198
|
+
throw new CommandAbort({
|
|
43495
44199
|
code: "NO_APP_LINKED",
|
|
43496
44200
|
message: [
|
|
43497
44201
|
`No ${APP_CONFIG_FILENAME} here \u2014 this project isn't linked to a Whop app yet.`,
|
|
43498
44202
|
"",
|
|
43499
|
-
`${reuse}To
|
|
44203
|
+
`${reuse}To register a new app for this existing code, ask the user for a name and route (the route becomes <route>.whop.app), then run:`,
|
|
43500
44204
|
' whop apps create --name "My Site" --route my-site',
|
|
43501
44205
|
"",
|
|
43502
44206
|
"Then deploy with the returned app id:",
|
|
@@ -43507,7 +44211,7 @@ To reuse one: whop apps deploy --app <app_id>
|
|
|
43507
44211
|
commands: [
|
|
43508
44212
|
{
|
|
43509
44213
|
command: "apps create",
|
|
43510
|
-
description: "
|
|
44214
|
+
description: "Register a new app (pass --name and --route)"
|
|
43511
44215
|
},
|
|
43512
44216
|
{
|
|
43513
44217
|
command: "apps deploy",
|
|
@@ -43522,27 +44226,41 @@ To reuse one: whop apps deploy --app <app_id>
|
|
|
43522
44226
|
const recent = [...apps].sort(
|
|
43523
44227
|
(a, b) => (b.created_at ?? 0) - (a.created_at ?? 0)
|
|
43524
44228
|
);
|
|
43525
|
-
const choice = await
|
|
43526
|
-
|
|
43527
|
-
|
|
44229
|
+
const choice = await promptSelect(
|
|
44230
|
+
"Which app should this project deploy to?",
|
|
44231
|
+
[
|
|
43528
44232
|
...recent.map((a) => ({
|
|
43529
44233
|
value: a.id,
|
|
43530
44234
|
label: `${a.name} (${a.id}${a.route ? `, ${a.route}.whop.app` : ""})`
|
|
43531
44235
|
})),
|
|
43532
|
-
{ value: "__new__", label: "
|
|
44236
|
+
{ value: "__new__", label: "Register a new app" }
|
|
43533
44237
|
]
|
|
43534
|
-
|
|
43535
|
-
if (isCancel3(choice)) cancelled();
|
|
44238
|
+
);
|
|
43536
44239
|
if (choice !== "__new__") {
|
|
43537
44240
|
selected = recent.find((a) => a.id === choice) ?? null;
|
|
43538
44241
|
}
|
|
43539
44242
|
}
|
|
43540
44243
|
if (!selected) {
|
|
43541
|
-
const
|
|
44244
|
+
const name = await promptText("What is your app called?", "My Site");
|
|
44245
|
+
if (name === null) cancelled();
|
|
44246
|
+
const routeInput = await promptText(
|
|
44247
|
+
"Which route should it live at? (your-route.whop.app)",
|
|
44248
|
+
slugify3(name),
|
|
44249
|
+
slugify3(name)
|
|
44250
|
+
);
|
|
44251
|
+
if (routeInput === null) cancelled();
|
|
44252
|
+
const route = slugify3(routeInput);
|
|
44253
|
+
if (!route) {
|
|
44254
|
+
throw new CommandAbort({
|
|
44255
|
+
code: "ROUTE_REQUIRED",
|
|
44256
|
+
message: `Couldn't derive a route from "${routeInput}" \u2014 use letters and numbers (e.g. my-site).`,
|
|
44257
|
+
retryable: true
|
|
44258
|
+
});
|
|
44259
|
+
}
|
|
43542
44260
|
try {
|
|
43543
44261
|
selected = await createApp({ name, route });
|
|
43544
44262
|
} catch (err2) {
|
|
43545
|
-
throw new
|
|
44263
|
+
throw new CommandAbort({
|
|
43546
44264
|
code: "CREATE_FAILED",
|
|
43547
44265
|
message: err2 instanceof Error ? err2.message : "Failed to register the app",
|
|
43548
44266
|
retryable: true
|
|
@@ -43553,7 +44271,7 @@ To reuse one: whop apps deploy --app <app_id>
|
|
|
43553
44271
|
writeAppConfig(projectDir, config);
|
|
43554
44272
|
log2(
|
|
43555
44273
|
c2,
|
|
43556
|
-
|
|
44274
|
+
chalk2.dim(
|
|
43557
44275
|
`Linked ${config.name} (${config.app_id}) \u2192 ${APP_CONFIG_FILENAME}`
|
|
43558
44276
|
)
|
|
43559
44277
|
);
|
|
@@ -43562,7 +44280,7 @@ To reuse one: whop apps deploy --app <app_id>
|
|
|
43562
44280
|
async function withRoute(c2, app) {
|
|
43563
44281
|
if (app.route) return { app_id: app.id, name: app.name, route: app.route };
|
|
43564
44282
|
if (c2.agent) {
|
|
43565
|
-
throw new
|
|
44283
|
+
throw new CommandAbort({
|
|
43566
44284
|
code: "ROUTE_REQUIRED",
|
|
43567
44285
|
message: `App ${app.id} (${app.name}) has no route \u2014 hosted apps are served from <route>.whop.app. Ask the user which route to claim, then run:
|
|
43568
44286
|
whop apps update ${app.id} --route <route>
|
|
@@ -43587,7 +44305,7 @@ async function withRoute(c2, app) {
|
|
|
43587
44305
|
if (routeInput === null) cancelled();
|
|
43588
44306
|
const route = slugify3(routeInput);
|
|
43589
44307
|
if (!route) {
|
|
43590
|
-
throw new
|
|
44308
|
+
throw new CommandAbort({
|
|
43591
44309
|
code: "ROUTE_REQUIRED",
|
|
43592
44310
|
message: `Couldn't derive a route from "${routeInput}" \u2014 use letters and numbers (e.g. my-site).`,
|
|
43593
44311
|
retryable: true
|
|
@@ -43601,151 +44319,35 @@ async function withRoute(c2, app) {
|
|
|
43601
44319
|
route: updated.route ?? route
|
|
43602
44320
|
};
|
|
43603
44321
|
} catch (err2) {
|
|
43604
|
-
throw new
|
|
44322
|
+
throw new CommandAbort({
|
|
43605
44323
|
code: "UPDATE_FAILED",
|
|
43606
44324
|
message: err2 instanceof Error ? err2.message : "Failed to set the route",
|
|
43607
44325
|
retryable: true
|
|
43608
44326
|
});
|
|
43609
44327
|
}
|
|
43610
44328
|
}
|
|
43611
|
-
|
|
43612
|
-
|
|
44329
|
+
function advanceRemoteSnapshot(c2, projectDir, appId, buildId, sourceZip) {
|
|
44330
|
+
if (!gitInstalled() || repoStatus(projectDir).kind !== "root") return;
|
|
44331
|
+
const snapshotDir = mkdtempSync3(join10(tmpdir3(), "whop-deploy-"));
|
|
43613
44332
|
try {
|
|
43614
|
-
|
|
43615
|
-
|
|
43616
|
-
|
|
43617
|
-
|
|
43618
|
-
|
|
43619
|
-
|
|
43620
|
-
|
|
43621
|
-
}
|
|
43622
|
-
const sourceUrl = template.production_web_build?.source_url;
|
|
43623
|
-
if (!sourceUrl) {
|
|
43624
|
-
throw new DeployAbort({
|
|
43625
|
-
code: "TEMPLATE_HAS_NO_SOURCE",
|
|
43626
|
-
message: `${template.name} (${template.id}) has no published source archive, so it can't be used as a template.`
|
|
43627
|
-
});
|
|
43628
|
-
}
|
|
43629
|
-
let config;
|
|
43630
|
-
if (appOption) {
|
|
43631
|
-
let app;
|
|
43632
|
-
try {
|
|
43633
|
-
app = await getApp(appOption);
|
|
43634
|
-
} catch (err2) {
|
|
43635
|
-
throw new DeployAbort({
|
|
43636
|
-
code: "APP_NOT_FOUND",
|
|
43637
|
-
message: `Could not load app ${appOption}: ${err2 instanceof Error ? err2.message : "unknown error"}`,
|
|
43638
|
-
retryable: true
|
|
43639
|
-
});
|
|
43640
|
-
}
|
|
43641
|
-
config = await withRoute(c2, app);
|
|
43642
|
-
} else if (c2.agent) {
|
|
43643
|
-
throw new DeployAbort({
|
|
43644
|
-
code: "TEMPLATE_NEEDS_APP",
|
|
43645
|
-
message: [
|
|
43646
|
-
"Cloning a template needs a target app. Ask the user for a name and route (the route becomes <route>.whop.app), then run:",
|
|
43647
|
-
' whop apps create --name "My Site" --route my-site',
|
|
43648
|
-
"",
|
|
43649
|
-
"Then deploy the template into it:",
|
|
43650
|
-
` whop apps deploy --template ${templateId} --app <app_id>`
|
|
43651
|
-
].join("\n"),
|
|
43652
|
-
retryable: true,
|
|
43653
|
-
cta: {
|
|
43654
|
-
commands: [
|
|
43655
|
-
{
|
|
43656
|
-
command: "apps create",
|
|
43657
|
-
description: "Create the new app (pass --name and --route)"
|
|
43658
|
-
},
|
|
43659
|
-
{
|
|
43660
|
-
command: `apps deploy --template ${templateId}`,
|
|
43661
|
-
description: "Re-run with --app <app_id> to clone and deploy"
|
|
43662
|
-
}
|
|
43663
|
-
]
|
|
43664
|
-
}
|
|
43665
|
-
});
|
|
43666
|
-
} else {
|
|
43667
|
-
const { name, route } = await promptForNameAndRoute();
|
|
43668
|
-
let app;
|
|
43669
|
-
try {
|
|
43670
|
-
app = await createApp({ name, route });
|
|
43671
|
-
} catch (err2) {
|
|
43672
|
-
throw new DeployAbort({
|
|
43673
|
-
code: "CREATE_FAILED",
|
|
43674
|
-
message: err2 instanceof Error ? err2.message : "Failed to register the app",
|
|
43675
|
-
retryable: true
|
|
43676
|
-
});
|
|
43677
|
-
}
|
|
43678
|
-
config = { app_id: app.id, name, route };
|
|
43679
|
-
}
|
|
43680
|
-
const targetDir = dirOption ? resolve6(dirOption) : resolve6(`./${config.route}`);
|
|
43681
|
-
if (existsSync5(targetDir) && readdirSync3(targetDir).length > 0) {
|
|
43682
|
-
throw new DeployAbort({
|
|
43683
|
-
code: "TARGET_NOT_EMPTY",
|
|
43684
|
-
message: `Target directory is not empty: ${targetDir}. Pick a different route or pass --dir <empty-dir>.`
|
|
43685
|
-
});
|
|
43686
|
-
}
|
|
43687
|
-
const spin = c2.agent ? null : spinner2();
|
|
43688
|
-
spin?.start(`Cloning ${template.name} (${template.id}) source archive`);
|
|
43689
|
-
try {
|
|
43690
|
-
const response = await fetch(sourceUrl);
|
|
43691
|
-
if (!response.ok) {
|
|
43692
|
-
throw new Error(
|
|
43693
|
-
`Download failed: ${response.status} ${response.statusText}`
|
|
43694
|
-
);
|
|
43695
|
-
}
|
|
43696
|
-
const zip = new Uint8Array(await response.arrayBuffer());
|
|
43697
|
-
const fileCount = extractSourceArchive(zip, targetDir);
|
|
43698
|
-
writeAppConfig(targetDir, config);
|
|
43699
|
-
spin?.stop(
|
|
43700
|
-
`Cloned ${fileCount} files from ${template.name} \u2192 ${targetDir}`
|
|
44333
|
+
extractSourceArchive(sourceZip, snapshotDir);
|
|
44334
|
+
commitSnapshot(
|
|
44335
|
+
projectDir,
|
|
44336
|
+
snapshotDir,
|
|
44337
|
+
appId,
|
|
44338
|
+
`whop apps deploy: ${appId} @ ${buildId}`,
|
|
44339
|
+
{ deriveFromHead: true }
|
|
43701
44340
|
);
|
|
43702
44341
|
} catch (err2) {
|
|
43703
|
-
|
|
43704
|
-
|
|
43705
|
-
|
|
43706
|
-
|
|
43707
|
-
|
|
43708
|
-
|
|
43709
|
-
|
|
43710
|
-
});
|
|
44342
|
+
log2(
|
|
44343
|
+
c2,
|
|
44344
|
+
chalk2.dim(
|
|
44345
|
+
`Couldn't record the deploy snapshot for \`whop apps pull\` (${err2 instanceof Error ? err2.message : "unknown error"}).`
|
|
44346
|
+
)
|
|
44347
|
+
);
|
|
44348
|
+
} finally {
|
|
44349
|
+
rmSync4(snapshotDir, { recursive: true, force: true });
|
|
43711
44350
|
}
|
|
43712
|
-
return { projectDir: targetDir, config };
|
|
43713
|
-
}
|
|
43714
|
-
function resolveLinkedAppId(appOption) {
|
|
43715
|
-
if (appOption) return appOption;
|
|
43716
|
-
const projectDir = findProjectDir();
|
|
43717
|
-
const config = projectDir ? readAppConfig(projectDir) : null;
|
|
43718
|
-
if (config) return config.app_id;
|
|
43719
|
-
throw new Error(
|
|
43720
|
-
`No app specified. Pass --app app_xxx or run inside a project with ${APP_CONFIG_FILENAME}.`
|
|
43721
|
-
);
|
|
43722
|
-
}
|
|
43723
|
-
var DURATION_MS = {
|
|
43724
|
-
s: 1e3,
|
|
43725
|
-
m: 6e4,
|
|
43726
|
-
h: 36e5,
|
|
43727
|
-
d: 864e5
|
|
43728
|
-
};
|
|
43729
|
-
function parseTimeOption(value) {
|
|
43730
|
-
if (!value) return void 0;
|
|
43731
|
-
const match = /^(\d+)([smhd])$/.exec(value);
|
|
43732
|
-
if (!match) return value;
|
|
43733
|
-
return new Date(
|
|
43734
|
-
Date.now() - Number(match[1]) * DURATION_MS[match[2]]
|
|
43735
|
-
).toISOString();
|
|
43736
|
-
}
|
|
43737
|
-
var LOG_LEVEL_COLORS = {
|
|
43738
|
-
error: chalk.red,
|
|
43739
|
-
warn: chalk.yellow
|
|
43740
|
-
};
|
|
43741
|
-
function printLogLine(row) {
|
|
43742
|
-
const time = row.created_at.replace("T", " ").replace("Z", "");
|
|
43743
|
-
const color = LOG_LEVEL_COLORS[row.level] ?? chalk.dim;
|
|
43744
|
-
const request2 = row.request_method ? chalk.dim(` ${row.request_method} ${row.request_path ?? ""}`) : "";
|
|
43745
|
-
console.log(
|
|
43746
|
-
`${chalk.dim(time)} ${color(row.level.padEnd(5))}${request2} ${row.message}`
|
|
43747
|
-
);
|
|
43748
|
-
if (row.stack) console.log(chalk.dim(row.stack));
|
|
43749
44351
|
}
|
|
43750
44352
|
function buildSecretsGroup() {
|
|
43751
44353
|
const secrets = Cli_exports.create("secrets", {
|
|
@@ -43809,9 +44411,18 @@ function buildSecretsGroup() {
|
|
|
43809
44411
|
});
|
|
43810
44412
|
return secrets;
|
|
43811
44413
|
}
|
|
44414
|
+
function resolveLinkedAppId(appOption) {
|
|
44415
|
+
if (appOption) return appOption;
|
|
44416
|
+
const projectDir = findProjectDir();
|
|
44417
|
+
const config = projectDir ? readAppConfig(projectDir) : null;
|
|
44418
|
+
if (config) return config.app_id;
|
|
44419
|
+
throw new Error(
|
|
44420
|
+
`No app specified. Pass --app app_xxx or run inside a project with ${APP_CONFIG_FILENAME}.`
|
|
44421
|
+
);
|
|
44422
|
+
}
|
|
43812
44423
|
async function buildAppGroup() {
|
|
43813
44424
|
const app = Cli_exports.create("apps", {
|
|
43814
|
-
description: "Build and deploy fully-hosted web apps on Whop (*.whop.app)"
|
|
44425
|
+
description: "Build and deploy fully-hosted web apps on Whop (*.whop.app) \u2014 Vite apps served from Cloudflare Workers"
|
|
43815
44426
|
});
|
|
43816
44427
|
await registerSpecCommands(app, "Apps");
|
|
43817
44428
|
const builds = Cli_exports.create("builds", {
|
|
@@ -43820,52 +44431,129 @@ async function buildAppGroup() {
|
|
|
43820
44431
|
await registerSpecCommands(builds, "App builds");
|
|
43821
44432
|
app.command(builds);
|
|
43822
44433
|
app.command(buildSecretsGroup());
|
|
43823
|
-
app.command("
|
|
43824
|
-
description: "
|
|
43825
|
-
hint: "
|
|
43826
|
-
options:
|
|
43827
|
-
|
|
43828
|
-
|
|
43829
|
-
query: external_exports.string().optional().describe("Only logs whose message contains this text"),
|
|
43830
|
-
build: external_exports.string().optional().describe("Only logs from this build (abld_xxx)"),
|
|
43831
|
-
since: external_exports.string().optional().describe(
|
|
43832
|
-
'Start of the window \u2014 a duration like "10m"/"2h"/"1d" or an ISO 8601 timestamp'
|
|
43833
|
-
),
|
|
43834
|
-
until: external_exports.string().optional().describe("End of the window \u2014 a duration or ISO 8601 timestamp"),
|
|
43835
|
-
first: external_exports.number().optional().describe("Number of log lines to return (max 500)"),
|
|
43836
|
-
after: external_exports.string().optional().describe("Cursor from a previous page's page_info")
|
|
43837
|
-
}),
|
|
44434
|
+
app.command("init", {
|
|
44435
|
+
description: "Start a new app: register, scaffold, and link a project",
|
|
44436
|
+
hint: "Registers the app on Whop (claiming <route>.whop.app), scaffolds the latest TanStack Start template \u2014 or a template app's published source with --template \u2014 wires it for Whop hosting, installs dependencies, and git-inits. Then `whop apps dev` to build, `whop apps deploy` to ship.",
|
|
44437
|
+
options: initOptions,
|
|
44438
|
+
output: initOutput,
|
|
44439
|
+
outputPolicy: "agent-only",
|
|
43838
44440
|
examples: [
|
|
43839
|
-
{ description: "
|
|
44441
|
+
{ description: "Scaffold a new app interactively" },
|
|
43840
44442
|
{
|
|
43841
|
-
options: {
|
|
43842
|
-
description: "
|
|
44443
|
+
options: { name: "My Site", route: "my-site" },
|
|
44444
|
+
description: "Scaffold without prompts (agents: always pass --name)"
|
|
43843
44445
|
},
|
|
43844
44446
|
{
|
|
43845
|
-
options: {
|
|
43846
|
-
description: "
|
|
44447
|
+
options: { name: "My Site", template: "app_xxxxxxxx" },
|
|
44448
|
+
description: "Start from another app's published source archive"
|
|
43847
44449
|
}
|
|
43848
44450
|
],
|
|
44451
|
+
run: async (c2) => {
|
|
44452
|
+
try {
|
|
44453
|
+
const result = await runInitFlow(c2, c2.options);
|
|
44454
|
+
const cdTarget = relative2(process.cwd(), result.projectDir) || ".";
|
|
44455
|
+
if (!c2.agent && !c2.formatExplicit) {
|
|
44456
|
+
console.log(
|
|
44457
|
+
[
|
|
44458
|
+
"",
|
|
44459
|
+
chalk2.green.bold(`\u2713 Created ${result.config.name}`),
|
|
44460
|
+
` ${chalk2.dim(result.config.app_id)} ${result.config.route}.whop.app`,
|
|
44461
|
+
"",
|
|
44462
|
+
chalk2.bold("Next"),
|
|
44463
|
+
...cdTarget !== "." ? [` cd ${cdTarget}`] : [],
|
|
44464
|
+
` ${chalk2.cyan.bold("whop apps dev")} ${chalk2.dim("\u2192 local dev server")}`,
|
|
44465
|
+
` ${chalk2.cyan.bold("whop apps deploy")} ${chalk2.dim("\u2192 ship it live")}`,
|
|
44466
|
+
""
|
|
44467
|
+
].join("\n")
|
|
44468
|
+
);
|
|
44469
|
+
}
|
|
44470
|
+
return c2.ok(
|
|
44471
|
+
{
|
|
44472
|
+
app_id: result.config.app_id,
|
|
44473
|
+
name: result.config.name,
|
|
44474
|
+
route: result.config.route,
|
|
44475
|
+
dir: result.projectDir,
|
|
44476
|
+
manual_steps: result.manualSteps.length ? result.manualSteps : void 0
|
|
44477
|
+
},
|
|
44478
|
+
{
|
|
44479
|
+
cta: {
|
|
44480
|
+
commands: [
|
|
44481
|
+
{ command: "apps dev", description: "Run the dev server" },
|
|
44482
|
+
{ command: "apps deploy", description: "Ship it live" }
|
|
44483
|
+
]
|
|
44484
|
+
}
|
|
44485
|
+
}
|
|
44486
|
+
);
|
|
44487
|
+
} catch (err2) {
|
|
44488
|
+
if (err2 instanceof CommandAbort) return c2.error(err2.options);
|
|
44489
|
+
return c2.error({
|
|
44490
|
+
code: "INIT_FAILED",
|
|
44491
|
+
message: err2 instanceof Error ? err2.message : "Init failed",
|
|
44492
|
+
retryable: true
|
|
44493
|
+
});
|
|
44494
|
+
}
|
|
44495
|
+
}
|
|
44496
|
+
});
|
|
44497
|
+
app.command("pull", {
|
|
44498
|
+
description: "Fetch the app's deployed source and merge it into this directory",
|
|
44499
|
+
hint: "Downloads the current production build's source archive (or --build abld_xxx for any specific build \u2014 including rollback inspection) and three-way merges it with your local code via git: a per-app snapshot ref supplies the merge base, local edits are autostashed around the merge, and conflicts surface as normal git conflict markers. On a fresh machine it creates ./<route> and becomes your working copy.",
|
|
44500
|
+
options: pullOptions,
|
|
44501
|
+
output: pullOutput,
|
|
43849
44502
|
outputPolicy: "agent-only",
|
|
44503
|
+
examples: [
|
|
44504
|
+
{ description: "Pull the linked app's deployed source" },
|
|
44505
|
+
{
|
|
44506
|
+
options: { app: "app_xxxxxxxx" },
|
|
44507
|
+
description: "Get an existing app's code onto a fresh machine"
|
|
44508
|
+
},
|
|
44509
|
+
{
|
|
44510
|
+
options: { build: "abld_xxxxxxxx" },
|
|
44511
|
+
description: "Materialize a specific build's source"
|
|
44512
|
+
},
|
|
44513
|
+
{
|
|
44514
|
+
options: { overwrite: true },
|
|
44515
|
+
description: "Skip the merge and write the files as-is"
|
|
44516
|
+
}
|
|
44517
|
+
],
|
|
43850
44518
|
run: async (c2) => {
|
|
43851
|
-
|
|
43852
|
-
|
|
43853
|
-
|
|
43854
|
-
|
|
43855
|
-
|
|
43856
|
-
|
|
43857
|
-
|
|
43858
|
-
|
|
43859
|
-
|
|
43860
|
-
|
|
43861
|
-
|
|
43862
|
-
|
|
43863
|
-
|
|
43864
|
-
|
|
44519
|
+
try {
|
|
44520
|
+
const result = await runPullFlow(c2.options);
|
|
44521
|
+
if (!c2.agent && !c2.formatExplicit) {
|
|
44522
|
+
const summary = {
|
|
44523
|
+
merged: "\u2713 Pulled and merged the deployed source",
|
|
44524
|
+
up_to_date: "\u2713 Already up to date",
|
|
44525
|
+
overwritten: "\u2713 Wrote the deployed source over the working tree"
|
|
44526
|
+
}[result.status];
|
|
44527
|
+
console.log(
|
|
44528
|
+
[
|
|
44529
|
+
"",
|
|
44530
|
+
chalk2.green.bold(summary),
|
|
44531
|
+
` ${chalk2.dim(`${result.app_id}${result.build_id ? ` @ ${result.build_id}` : ""}`)} ${result.dir}`,
|
|
44532
|
+
...result.autostash === "kept" ? [
|
|
44533
|
+
chalk2.yellow(
|
|
44534
|
+
" Your uncommitted changes are still stashed (`git stash pop` conflicted) \u2014 pop them when ready."
|
|
44535
|
+
)
|
|
44536
|
+
] : [],
|
|
44537
|
+
...result.status === "merged" ? [chalk2.dim(" Review with `git log` / `git diff HEAD~1`.")] : [],
|
|
44538
|
+
""
|
|
44539
|
+
].join("\n")
|
|
44540
|
+
);
|
|
43865
44541
|
}
|
|
43866
|
-
|
|
44542
|
+
return c2.ok(result, {
|
|
44543
|
+
cta: {
|
|
44544
|
+
commands: [
|
|
44545
|
+
{ command: "apps dev", description: "Run the dev server" }
|
|
44546
|
+
]
|
|
44547
|
+
}
|
|
44548
|
+
});
|
|
44549
|
+
} catch (err2) {
|
|
44550
|
+
if (err2 instanceof CommandAbort) return c2.error(err2.options);
|
|
44551
|
+
return c2.error({
|
|
44552
|
+
code: "PULL_FAILED",
|
|
44553
|
+
message: err2 instanceof Error ? err2.message : "Pull failed",
|
|
44554
|
+
retryable: true
|
|
44555
|
+
});
|
|
43867
44556
|
}
|
|
43868
|
-
return c2.ok({ app_id: appId, logs, page_info: page.page_info });
|
|
43869
44557
|
}
|
|
43870
44558
|
});
|
|
43871
44559
|
app.command("dev", {
|
|
@@ -43875,17 +44563,18 @@ async function buildAppGroup() {
|
|
|
43875
44563
|
dir: external_exports.string().optional().describe("Project directory (defaults to the current directory)")
|
|
43876
44564
|
}),
|
|
43877
44565
|
run: async (c2) => {
|
|
43878
|
-
const projectDir = c2.options.dir ?
|
|
44566
|
+
const projectDir = c2.options.dir ? resolve8(c2.options.dir) : findProjectDir();
|
|
43879
44567
|
const config = projectDir ? readAppConfig(projectDir) : null;
|
|
43880
44568
|
if (!projectDir || !config) {
|
|
43881
44569
|
return c2.error({
|
|
43882
44570
|
code: "NO_PROJECT",
|
|
43883
|
-
message: `No ${APP_CONFIG_FILENAME} found in this directory or any parent. Run \`whop apps
|
|
44571
|
+
message: `No ${APP_CONFIG_FILENAME} found in this directory or any parent. Run \`whop apps init\` to start a new app, or \`whop apps pull --app app_xxx\` to fetch an existing one.`,
|
|
43884
44572
|
cta: {
|
|
43885
44573
|
commands: [
|
|
44574
|
+
{ command: "apps init", description: "Start a new app" },
|
|
43886
44575
|
{
|
|
43887
|
-
command: "apps
|
|
43888
|
-
description: "
|
|
44576
|
+
command: "apps pull",
|
|
44577
|
+
description: "Fetch an existing app's source"
|
|
43889
44578
|
}
|
|
43890
44579
|
]
|
|
43891
44580
|
}
|
|
@@ -43897,7 +44586,7 @@ async function buildAppGroup() {
|
|
|
43897
44586
|
if (process.env.WHOP_API_KEY) {
|
|
43898
44587
|
log2(
|
|
43899
44588
|
c2,
|
|
43900
|
-
|
|
44589
|
+
chalk2.dim(
|
|
43901
44590
|
"Using the WHOP_API_KEY from your environment for server-side calls."
|
|
43902
44591
|
)
|
|
43903
44592
|
);
|
|
@@ -43909,14 +44598,14 @@ async function buildAppGroup() {
|
|
|
43909
44598
|
const expiry = expiresAt ? ` (expires ${expiresAt.toLocaleTimeString()})` : "";
|
|
43910
44599
|
log2(
|
|
43911
44600
|
c2,
|
|
43912
|
-
|
|
44601
|
+
chalk2.dim(
|
|
43913
44602
|
`Injected a temporary access token as WHOP_API_KEY${expiry} \u2014 server-side SDK calls just work. Restart \`whop apps dev\` to refresh it.`
|
|
43914
44603
|
)
|
|
43915
44604
|
);
|
|
43916
44605
|
} catch (err2) {
|
|
43917
44606
|
log2(
|
|
43918
44607
|
c2,
|
|
43919
|
-
|
|
44608
|
+
chalk2.yellow(
|
|
43920
44609
|
`Couldn't mint a temporary access token (${err2 instanceof Error ? err2.message : "unknown error"}) \u2014 set WHOP_API_KEY manually for server-side calls.`
|
|
43921
44610
|
)
|
|
43922
44611
|
);
|
|
@@ -43934,7 +44623,7 @@ async function buildAppGroup() {
|
|
|
43934
44623
|
if (unsafe.length > 0) {
|
|
43935
44624
|
log2(
|
|
43936
44625
|
c2,
|
|
43937
|
-
|
|
44626
|
+
chalk2.yellow(
|
|
43938
44627
|
`Skipped app secret${unsafe.length === 1 ? "" : "s"} ${unsafe.map(([key]) => key).join(", ")} \u2014 these names control the local runtime and are never injected by \`whop apps dev\`.`
|
|
43939
44628
|
)
|
|
43940
44629
|
);
|
|
@@ -43943,7 +44632,7 @@ async function buildAppGroup() {
|
|
|
43943
44632
|
if (injectedCount > 0) {
|
|
43944
44633
|
log2(
|
|
43945
44634
|
c2,
|
|
43946
|
-
|
|
44635
|
+
chalk2.dim(
|
|
43947
44636
|
`Injected ${injectedCount} app secret${injectedCount === 1 ? "" : "s"} into the environment (${secretEntries.filter(([key]) => !isUnsafeDevEnvKey(key)).map(([key]) => key).join(", ")}).`
|
|
43948
44637
|
)
|
|
43949
44638
|
);
|
|
@@ -43951,12 +44640,12 @@ async function buildAppGroup() {
|
|
|
43951
44640
|
} catch (err2) {
|
|
43952
44641
|
log2(
|
|
43953
44642
|
c2,
|
|
43954
|
-
|
|
44643
|
+
chalk2.yellow(
|
|
43955
44644
|
`Couldn't fetch app secrets (${err2 instanceof Error ? err2.message : "unknown error"}) \u2014 continuing without them.`
|
|
43956
44645
|
)
|
|
43957
44646
|
);
|
|
43958
44647
|
}
|
|
43959
|
-
log2(c2,
|
|
44648
|
+
log2(c2, chalk2.dim(`Starting dev server for ${config.name}...`));
|
|
43960
44649
|
try {
|
|
43961
44650
|
runScript(projectDir, "dev", injected);
|
|
43962
44651
|
} catch (err2) {
|
|
@@ -43970,16 +44659,13 @@ async function buildAppGroup() {
|
|
|
43970
44659
|
});
|
|
43971
44660
|
app.command("deploy", {
|
|
43972
44661
|
description: "Build, upload and ship this app live",
|
|
43973
|
-
hint: "
|
|
44662
|
+
hint: "Builds the linked project, typechecks, uploads the build + source archives, and promotes to production. Use --preview to upload without promoting, then promote later with `whop apps builds promote <build_id>` \u2014 promoting an older build is also how you roll back. To start a new project use `whop apps init`; to fetch an existing app's code use `whop apps pull`.",
|
|
43974
44663
|
options: external_exports.object({
|
|
43975
44664
|
dir: external_exports.string().optional().describe("Project directory (defaults to the current directory)"),
|
|
43976
44665
|
app: external_exports.string().optional().describe(
|
|
43977
44666
|
"Link this project to an app (app_xxx) before deploying \u2014 writes whop.app.json, replacing any existing link"
|
|
43978
44667
|
),
|
|
43979
|
-
|
|
43980
|
-
"Create a new app from a template app (app_xxx): clones its published source archive into a fresh directory and deploys it"
|
|
43981
|
-
),
|
|
43982
|
-
skip_promote: external_exports.boolean().optional().describe("Upload the build without promoting it to production"),
|
|
44668
|
+
preview: external_exports.boolean().optional().describe("Upload the build without promoting it to production"),
|
|
43983
44669
|
skip_typecheck: external_exports.boolean().optional().describe("Skip the typecheck step"),
|
|
43984
44670
|
skip_build: external_exports.boolean().optional().describe("Skip the build step and upload the existing dist/ output")
|
|
43985
44671
|
}),
|
|
@@ -43992,36 +44678,18 @@ async function buildAppGroup() {
|
|
|
43992
44678
|
description: "Link this project to an existing app, then deploy"
|
|
43993
44679
|
},
|
|
43994
44680
|
{
|
|
43995
|
-
options: {
|
|
43996
|
-
description: "Create a new app from a template app's source and deploy it"
|
|
43997
|
-
},
|
|
43998
|
-
{
|
|
43999
|
-
options: { skip_promote: true },
|
|
44681
|
+
options: { preview: true },
|
|
44000
44682
|
description: "Upload a preview build without promoting"
|
|
44001
44683
|
}
|
|
44002
44684
|
],
|
|
44003
44685
|
run: async (c2) => {
|
|
44004
44686
|
try {
|
|
44005
|
-
|
|
44006
|
-
|
|
44007
|
-
|
|
44008
|
-
|
|
44009
|
-
|
|
44010
|
-
|
|
44011
|
-
c2,
|
|
44012
|
-
c2.options.template,
|
|
44013
|
-
c2.options.app,
|
|
44014
|
-
c2.options.dir
|
|
44015
|
-
));
|
|
44016
|
-
freshlyScaffolded = true;
|
|
44017
|
-
} else {
|
|
44018
|
-
({ projectDir, freshlyScaffolded } = await ensureViteApp(
|
|
44019
|
-
c2,
|
|
44020
|
-
pm,
|
|
44021
|
-
c2.options.dir
|
|
44022
|
-
));
|
|
44023
|
-
config = await ensureAppLinked(c2, projectDir, c2.options.app);
|
|
44024
|
-
}
|
|
44687
|
+
ensurePackageManager();
|
|
44688
|
+
const { projectDir, freshInit } = await resolveDeployProject(
|
|
44689
|
+
c2,
|
|
44690
|
+
c2.options.dir
|
|
44691
|
+
);
|
|
44692
|
+
const config = freshInit ?? await ensureAppLinked(c2, projectDir, c2.options.app);
|
|
44025
44693
|
const { manualSteps } = whopifyProject({
|
|
44026
44694
|
targetDir: projectDir,
|
|
44027
44695
|
route: config.route
|
|
@@ -44037,38 +44705,38 @@ Then re-run \`whop apps deploy\` \u2014 it re-checks the wiring and applies anyt
|
|
|
44037
44705
|
});
|
|
44038
44706
|
}
|
|
44039
44707
|
if (!c2.options.skip_build) {
|
|
44040
|
-
if (
|
|
44708
|
+
if (!existsSync7(join10(projectDir, "node_modules", "@whop", "cli"))) {
|
|
44041
44709
|
runInstall(c2, projectDir);
|
|
44042
44710
|
}
|
|
44043
|
-
log2(c2,
|
|
44711
|
+
log2(c2, chalk2.dim("[1/4] Building..."));
|
|
44044
44712
|
runScript(projectDir, "build", { WHOP_APP_ID: config.app_id });
|
|
44045
44713
|
if (!c2.options.skip_typecheck && hasScript(projectDir, "typecheck")) {
|
|
44046
|
-
log2(c2,
|
|
44714
|
+
log2(c2, chalk2.dim("[2/4] Typechecking..."));
|
|
44047
44715
|
runScript(projectDir, "typecheck");
|
|
44048
44716
|
}
|
|
44049
44717
|
}
|
|
44050
|
-
const archivePath =
|
|
44051
|
-
if (!
|
|
44718
|
+
const archivePath = join10(projectDir, BUILD_ARCHIVE);
|
|
44719
|
+
if (!existsSync7(archivePath)) {
|
|
44052
44720
|
return c2.error({
|
|
44053
44721
|
code: "NO_BUILD_ARCHIVE",
|
|
44054
44722
|
message: `No build archive at ${BUILD_ARCHIVE}. Add the whop() plugin from @whop/cli/vite to your vite config, then build. See https://whop.com/docs/apps/hosting.`
|
|
44055
44723
|
});
|
|
44056
44724
|
}
|
|
44057
|
-
const zip =
|
|
44725
|
+
const zip = readFileSync7(archivePath);
|
|
44058
44726
|
const sizeMb = (zip.byteLength / 1024 / 1024).toFixed(1);
|
|
44059
44727
|
const checksum = createHash3("sha256").update(zip).digest("hex");
|
|
44060
44728
|
const sourceArchive = packSourceArchive(projectDir);
|
|
44061
44729
|
if ("skipped" in sourceArchive) {
|
|
44062
44730
|
log2(
|
|
44063
44731
|
c2,
|
|
44064
|
-
|
|
44732
|
+
chalk2.yellow(
|
|
44065
44733
|
`Skipping source archive upload: ${sourceArchive.skipped}`
|
|
44066
44734
|
)
|
|
44067
44735
|
);
|
|
44068
44736
|
} else if (sourceArchive.note) {
|
|
44069
|
-
log2(c2,
|
|
44737
|
+
log2(c2, chalk2.dim(`Source archive: ${sourceArchive.note}`));
|
|
44070
44738
|
}
|
|
44071
|
-
const spin = c2.agent ? null :
|
|
44739
|
+
const spin = c2.agent ? null : spinner4();
|
|
44072
44740
|
spin?.start(`[3/4] Uploading build archive (${sizeMb} MB)`);
|
|
44073
44741
|
let build;
|
|
44074
44742
|
try {
|
|
@@ -44102,8 +44770,17 @@ Then re-run \`whop apps deploy\` \u2014 it re-checks the wiring and applies anyt
|
|
|
44102
44770
|
spin?.error("[3/4] Upload failed");
|
|
44103
44771
|
throw err2;
|
|
44104
44772
|
}
|
|
44105
|
-
if (
|
|
44106
|
-
|
|
44773
|
+
if ("zip" in sourceArchive) {
|
|
44774
|
+
advanceRemoteSnapshot(
|
|
44775
|
+
c2,
|
|
44776
|
+
projectDir,
|
|
44777
|
+
config.app_id,
|
|
44778
|
+
build.id,
|
|
44779
|
+
sourceArchive.zip
|
|
44780
|
+
);
|
|
44781
|
+
}
|
|
44782
|
+
if (!c2.options.preview) {
|
|
44783
|
+
const promoteSpin = c2.agent ? null : spinner4();
|
|
44107
44784
|
promoteSpin?.start("[4/4] Promoting to production");
|
|
44108
44785
|
try {
|
|
44109
44786
|
await promoteAppBuild(build.id);
|
|
@@ -44116,7 +44793,7 @@ Then re-run \`whop apps deploy\` \u2014 it re-checks the wiring and applies anyt
|
|
|
44116
44793
|
throw err2;
|
|
44117
44794
|
}
|
|
44118
44795
|
} else {
|
|
44119
|
-
log2(c2,
|
|
44796
|
+
log2(c2, chalk2.dim("[4/4] Skipping promotion (--preview)"));
|
|
44120
44797
|
}
|
|
44121
44798
|
const promoted = build.is_production === true;
|
|
44122
44799
|
const productionUrl = promoted ? (await getApp(config.app_id)).hosted_url ?? void 0 : void 0;
|
|
@@ -44125,22 +44802,22 @@ Then re-run \`whop apps deploy\` \u2014 it re-checks the wiring and applies anyt
|
|
|
44125
44802
|
console.log(
|
|
44126
44803
|
[
|
|
44127
44804
|
"",
|
|
44128
|
-
|
|
44805
|
+
chalk2.green.bold(
|
|
44129
44806
|
promoted ? "\u2713 Deployed to production" : "\u2713 Build uploaded"
|
|
44130
44807
|
),
|
|
44131
|
-
` ${
|
|
44808
|
+
` ${chalk2.dim(build.id)}`,
|
|
44132
44809
|
"",
|
|
44133
44810
|
...promoted && productionUrl ? [
|
|
44134
|
-
` ${
|
|
44811
|
+
` ${chalk2.bold("Live")} ${chalk2.cyan.underline(productionUrl)}`
|
|
44135
44812
|
] : [
|
|
44136
|
-
`${
|
|
44813
|
+
`${chalk2.bold("Promote when ready")} ${chalk2.cyan.bold(`whop apps builds promote ${build.id}`)}`
|
|
44137
44814
|
],
|
|
44138
|
-
...
|
|
44815
|
+
...freshInit ? [
|
|
44139
44816
|
"",
|
|
44140
|
-
|
|
44817
|
+
chalk2.bold("Next"),
|
|
44141
44818
|
...cdTarget !== "." ? [` cd ${cdTarget}`] : [],
|
|
44142
|
-
` ${
|
|
44143
|
-
` ${
|
|
44819
|
+
` ${chalk2.cyan.bold("whop apps dev")} ${chalk2.dim("\u2192 local dev server")}`,
|
|
44820
|
+
` ${chalk2.cyan.bold("whop apps deploy")} ${chalk2.dim("\u2192 ship your changes")}`
|
|
44144
44821
|
] : [],
|
|
44145
44822
|
""
|
|
44146
44823
|
].join("\n")
|
|
@@ -44165,7 +44842,7 @@ Then re-run \`whop apps deploy\` \u2014 it re-checks the wiring and applies anyt
|
|
|
44165
44842
|
}
|
|
44166
44843
|
);
|
|
44167
44844
|
} catch (err2) {
|
|
44168
|
-
if (err2 instanceof
|
|
44845
|
+
if (err2 instanceof CommandAbort) return c2.error(err2.options);
|
|
44169
44846
|
return c2.error({
|
|
44170
44847
|
code: "DEPLOY_FAILED",
|
|
44171
44848
|
message: err2 instanceof Error ? err2.message : "Deploy failed",
|
|
@@ -44941,7 +45618,7 @@ function buildAuthGroup() {
|
|
|
44941
45618
|
}
|
|
44942
45619
|
|
|
44943
45620
|
// src/media/commands.ts
|
|
44944
|
-
import { spinner as
|
|
45621
|
+
import { spinner as spinner5 } from "@clack/prompts";
|
|
44945
45622
|
var POLL_INTERVAL_MS = 3e3;
|
|
44946
45623
|
var whopFetch2 = createWhopFetch();
|
|
44947
45624
|
async function buildMediaGroup(description) {
|
|
@@ -45042,7 +45719,7 @@ async function createGeneration(c2, entry, body) {
|
|
|
45042
45719
|
}
|
|
45043
45720
|
async function awaitGeneration(c2, getEntry, created, timeoutSeconds) {
|
|
45044
45721
|
const interactive = !c2.agent && process.stdout.isTTY;
|
|
45045
|
-
const progress = interactive ?
|
|
45722
|
+
const progress = interactive ? spinner5() : null;
|
|
45046
45723
|
progress?.start(`Generating ${created.media_type ?? "asset"}\u2026`);
|
|
45047
45724
|
const startedAt = Date.now();
|
|
45048
45725
|
let asset = created;
|
|
@@ -45093,102 +45770,28 @@ async function fetchAsset(c2, getEntry, id, progress) {
|
|
|
45093
45770
|
}
|
|
45094
45771
|
}
|
|
45095
45772
|
function sleep2(ms) {
|
|
45096
|
-
return new Promise((
|
|
45773
|
+
return new Promise((resolve9) => setTimeout(resolve9, ms));
|
|
45097
45774
|
}
|
|
45098
45775
|
|
|
45099
|
-
// package.json
|
|
45100
|
-
var package_default = {
|
|
45101
|
-
name: "@whop/cli",
|
|
45102
|
-
version: "0.10.1",
|
|
45103
|
-
description: "The Whop CLI \u2014 build and manage Whop apps from your terminal. Human and agent friendly.",
|
|
45104
|
-
keywords: [
|
|
45105
|
-
"agent",
|
|
45106
|
-
"apps",
|
|
45107
|
-
"cli",
|
|
45108
|
-
"developer",
|
|
45109
|
-
"whop"
|
|
45110
|
-
],
|
|
45111
|
-
homepage: "https://whop.com/developers/",
|
|
45112
|
-
bugs: "https://github.com/whopio/whop-public-cli/issues",
|
|
45113
|
-
license: "MIT",
|
|
45114
|
-
repository: {
|
|
45115
|
-
type: "git",
|
|
45116
|
-
url: "git+https://github.com/whopio/whop-public-cli.git"
|
|
45117
|
-
},
|
|
45118
|
-
bin: {
|
|
45119
|
-
whop: "dist/index.js",
|
|
45120
|
-
"whop-cli": "dist/index.js"
|
|
45121
|
-
},
|
|
45122
|
-
files: [
|
|
45123
|
-
"dist",
|
|
45124
|
-
"skills"
|
|
45125
|
-
],
|
|
45126
|
-
type: "module",
|
|
45127
|
-
exports: {
|
|
45128
|
-
".": "./dist/index.js",
|
|
45129
|
-
"./vite": {
|
|
45130
|
-
types: "./dist/vite.d.ts",
|
|
45131
|
-
import: "./dist/vite.js"
|
|
45132
|
-
}
|
|
45133
|
-
},
|
|
45134
|
-
publishConfig: {
|
|
45135
|
-
access: "public"
|
|
45136
|
-
},
|
|
45137
|
-
scripts: {
|
|
45138
|
-
build: "tsx scripts/sync-spec.ts && pnpm run clean && tsup",
|
|
45139
|
-
"build:binary": "tsx scripts/build-binary.ts",
|
|
45140
|
-
clean: "rm -rf dist",
|
|
45141
|
-
dev: "tsx scripts/sync-spec.ts && tsx src/index.ts",
|
|
45142
|
-
start: "node dist/index.js",
|
|
45143
|
-
"check-types": "tsx scripts/sync-spec.ts && tsc --noEmit",
|
|
45144
|
-
lint: "oxlint .",
|
|
45145
|
-
"lint:fix": "oxlint --fix --fix-suggestions && oxfmt",
|
|
45146
|
-
"ci:lint": "oxlint -f github .",
|
|
45147
|
-
format: "oxfmt",
|
|
45148
|
-
"sync-spec": "tsx scripts/sync-spec.ts",
|
|
45149
|
-
test: "tsx --test scripts/*.test.ts"
|
|
45150
|
-
},
|
|
45151
|
-
dependencies: {
|
|
45152
|
-
"@clack/prompts": "^1.5.1",
|
|
45153
|
-
"@napi-rs/keyring": "^1.3.0",
|
|
45154
|
-
"@whop/sdk": "^0.0.40",
|
|
45155
|
-
chalk: "5.4.1",
|
|
45156
|
-
yaml: "2.8.0"
|
|
45157
|
-
},
|
|
45158
|
-
devDependencies: {
|
|
45159
|
-
"@types/node": "25.3.5",
|
|
45160
|
-
fflate: "0.8.2",
|
|
45161
|
-
incur: "github:whopio/incur#b0550076a7a66790e4d30db38103d8a91602b6cb",
|
|
45162
|
-
tsup: "8.5.0",
|
|
45163
|
-
tsx: "4.19.4",
|
|
45164
|
-
typescript: "5.9.3",
|
|
45165
|
-
vite: "7.3.6"
|
|
45166
|
-
},
|
|
45167
|
-
engines: {
|
|
45168
|
-
node: ">=22"
|
|
45169
|
-
},
|
|
45170
|
-
packageManager: "pnpm@10.23.0"
|
|
45171
|
-
};
|
|
45172
|
-
|
|
45173
45776
|
// src/journey/quickstart.ts
|
|
45174
45777
|
import { AuthenticationError as AuthenticationError2, PermissionDeniedError as PermissionDeniedError2 } from "@whop/sdk";
|
|
45175
|
-
import
|
|
45778
|
+
import chalk4 from "chalk";
|
|
45176
45779
|
|
|
45177
45780
|
// src/lib/colors.ts
|
|
45178
|
-
import
|
|
45781
|
+
import chalk3 from "chalk";
|
|
45179
45782
|
var c = {
|
|
45180
|
-
heading:
|
|
45181
|
-
command:
|
|
45182
|
-
description:
|
|
45183
|
-
flag:
|
|
45184
|
-
tableHeader:
|
|
45185
|
-
separator:
|
|
45186
|
-
number:
|
|
45187
|
-
muted:
|
|
45188
|
-
id:
|
|
45189
|
-
link:
|
|
45190
|
-
success:
|
|
45191
|
-
error:
|
|
45783
|
+
heading: chalk3.bold,
|
|
45784
|
+
command: chalk3.bold,
|
|
45785
|
+
description: chalk3.dim,
|
|
45786
|
+
flag: chalk3.yellow,
|
|
45787
|
+
tableHeader: chalk3.bold,
|
|
45788
|
+
separator: chalk3.dim,
|
|
45789
|
+
number: chalk3.dim,
|
|
45790
|
+
muted: chalk3.dim,
|
|
45791
|
+
id: chalk3.cyan,
|
|
45792
|
+
link: chalk3.cyan.underline,
|
|
45793
|
+
success: chalk3.green,
|
|
45794
|
+
error: chalk3.red.bold
|
|
45192
45795
|
};
|
|
45193
45796
|
|
|
45194
45797
|
// src/journey/quickstart.ts
|
|
@@ -45301,17 +45904,17 @@ async function runOAuthQuickstart(ctx, dependencies, profile, businessName) {
|
|
|
45301
45904
|
);
|
|
45302
45905
|
}
|
|
45303
45906
|
const interactive = !ctx.agent && !ctx.formatExplicit;
|
|
45304
|
-
const
|
|
45907
|
+
const spinner6 = interactive ? dependencies.startSpinner("Loading your business accounts\u2026") : null;
|
|
45305
45908
|
let accounts;
|
|
45306
45909
|
try {
|
|
45307
45910
|
accounts = await dependencies.listOAuthAccounts(accessToken, {
|
|
45308
45911
|
stopAtId: ctx.options.account_id
|
|
45309
45912
|
});
|
|
45310
|
-
|
|
45913
|
+
spinner6?.stop(
|
|
45311
45914
|
`Found ${accounts.length} business account${accounts.length === 1 ? "" : "s"}.`
|
|
45312
45915
|
);
|
|
45313
45916
|
} catch (error) {
|
|
45314
|
-
|
|
45917
|
+
spinner6?.stop("Couldn't load your business accounts.");
|
|
45315
45918
|
if (error instanceof AuthenticationError2 || error instanceof PermissionDeniedError2) {
|
|
45316
45919
|
return ctx.error({
|
|
45317
45920
|
code: "ACCOUNT_ACCESS_DENIED",
|
|
@@ -45370,7 +45973,7 @@ async function runOAuthQuickstart(ctx, dependencies, profile, businessName) {
|
|
|
45370
45973
|
if (accounts.length === 0) {
|
|
45371
45974
|
console.log(
|
|
45372
45975
|
`
|
|
45373
|
-
${
|
|
45976
|
+
${chalk4.bold("You don't have a business account yet \u2014 let's create one.")}`
|
|
45374
45977
|
);
|
|
45375
45978
|
return createAndSelectAccount(
|
|
45376
45979
|
ctx,
|
|
@@ -45588,9 +46191,9 @@ function printSummary(ctx, account, created) {
|
|
|
45588
46191
|
const action = created ? "Created and selected" : "Using";
|
|
45589
46192
|
console.log(
|
|
45590
46193
|
`
|
|
45591
|
-
${c.success("\u2713")} ${action} business account ${
|
|
46194
|
+
${c.success("\u2713")} ${action} business account ${chalk4.bold(`"${title}"`)} ${chalk4.dim(`(${account.id})`)}`
|
|
45592
46195
|
);
|
|
45593
|
-
console.log(
|
|
46196
|
+
console.log(chalk4.dim(" Run `whop --help` to choose what to do next.\n"));
|
|
45594
46197
|
}
|
|
45595
46198
|
|
|
45596
46199
|
// src/lib/renderer.ts
|