@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/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((resolve7, reject) => {
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
- resolve7({ stdout, stderr });
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((resolve7) => setTimeout(resolve7, 5));
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-08-1"
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 an identity review 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",
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": "Identity review required before payouts and card issuing."
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: "company_id",
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
- "company_id",
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: "company_id missing"
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
- company_id: "biz_xxxxxxxxxxxxxx",
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
- billing_period: {
15412
- description: "Recurring billing interval in days, such as 30 for monthly or 365 for annual.",
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
- "integer",
15467
+ "string",
15415
15468
  "null"
15416
15469
  ]
15417
15470
  },
15418
- company_id: {
15419
- description: "Account ID for the inline plan, prefixed `biz_`. Defaults to the account resolved from the request.",
15471
+ billing_period: {
15472
+ description: "Recurring billing interval in days, such as 30 for monthly or 365 for annual.",
15420
15473
  type: [
15421
- "string",
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
- "company_id",
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
- "company_id",
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({\n apiKey: process.env['WHOP_API_KEY'], // This is the default and can be omitted\n});\n\nconst response = await client.partners.leaderboard();\n\nconsole.log(response.leaders);"
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 company to list products for.",
23229
+ description: "The unique identifier of the account to list products for.",
23089
23230
  in: "query",
23090
- name: "company_id",
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 company_id"
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-08-1",
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-08-1"
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
- post: {
36112
- description: "Upload a new build artifact for an app. The build must include a compiled code bundle for the specified platform.\n\nRequired permissions:\n - `developer:manage_builds`",
36113
- operationId: "create",
36114
- parameters: [],
36115
- requestBody: {
36116
- content: {
36117
- "application/json": {
36118
- schema: {
36119
- description: "Parameters for CreateAppBuild",
36120
- properties: {
36121
- ai_prompt_id: {
36122
- description: "The identifier of the AI prompt that generated this build, if applicable.",
36123
- example: "prmt_xxxxxxxxxxxxx",
36124
- type: [
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
- required: true
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, Whop } from "@whop/sdk";
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 = new Whop({ apiKey, baseURL: baseUrl });
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.10.1" : null;
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 fetch(`${oauthBaseUrl()}/oauth/token`, {
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 = new Whop2({ apiKey: accessToken, baseURL: apiBaseUrl() });
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 spinner4 = input.interactive ? dependencies.startSpinner("Exchanging authorization code\u2026") : null;
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
- spinner4?.stop("Authorized.");
41243
+ spinner6?.stop("Authorized.");
41089
41244
  } catch (error) {
41090
- spinner4?.stop("Token exchange failed.");
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 spinner4 = dependencies.startSpinner("Opening browser\u2026");
41298
+ const spinner6 = dependencies.startSpinner("Opening browser\u2026");
41144
41299
  dependencies.openBrowser(login.authorizationUrl);
41145
- spinner4.stop("Waiting for authorization in your browser\u2026");
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 fetch(target, { method: request2.method, headers, body });
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: "Identity review required before payouts and card issuing.",
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 { isCancel as isCancel3, select as select2, spinner as spinner2, text as text2 } from "@clack/prompts";
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 existsSync5, readFileSync as readFileSync6, readdirSync as readdirSync3 } from "fs";
41746
- import { join as join7, relative as relative2, resolve as resolve6 } from "path";
41747
- import chalk from "chalk";
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 whopFetch = createWhopFetch();
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 fetch(`${apiBaseUrl().replace(/\/$/, "")}${path8}`, {
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 whopFetch(new Request(`${PLACEHOLDER}${path8}`, init));
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((resolve7) => setTimeout(resolve7, ms));
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/source-archive.ts
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 join5, resolve as resolve5, sep } from "path";
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 = spawnSync(
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(join5(projectDir, path8)).isFile();
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(join5(projectDir, prefix), {
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(join5(projectDir, path8));
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 spawnSync2 } from "child_process";
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 join6 } from "path";
43000
- var SPAWN_VIA_SHELL = process.platform === "win32";
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 = spawnSync2(pm, ["--version"], {
43332
+ const result = spawnSync3(pm, ["--version"], {
43014
43333
  stdio: "ignore",
43015
- shell: SPAWN_VIA_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 = spawnSync2(
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: SPAWN_VIA_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) => join6(dir, name)).find(
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 = join6(dir, "package.json");
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) => join6(dir, name)).find(
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 ensurePackageManager() {
43351
- const pm = detectPackageManager();
43352
- if (pm) return pm;
43353
- throw new DeployAbort({
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 DeployAbort({
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
- "Ask the user whether to scaffold a new app here, or locate their existing Vite app.",
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 scaffold a new app (TanStack Start), run:",
43375
- ` ${tanstackCreateCommand(pm, "<app-name>")}`,
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 app directory.",
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 deploy",
43386
- description: "Re-run from inside a Vite app directory"
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 select2({
43393
- message: `${projectDir} doesn't look like a Vite app. What do you want to do?`,
43394
- options: [
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 (routeInput === null) cancelled();
43416
- const route = slugify3(routeInput);
43417
- if (!route) {
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 DeployAbort({
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
- chalk.dim(
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 DeployAbort({
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 create a new app, ask the user for a name and route (the route becomes <route>.whop.app), then run:`,
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: "Create a new app (pass --name and --route)"
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 select2({
43526
- message: "Which app should this project deploy to?",
43527
- options: [
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: "Create a new app" }
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 { name, route } = await promptForNameAndRoute();
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 DeployAbort({
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
- chalk.dim(
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 DeployAbort({
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 DeployAbort({
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 DeployAbort({
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
- async function scaffoldFromTemplate(c2, templateId, appOption, dirOption) {
43612
- let template;
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
- template = await getApp(templateId);
43615
- } catch (err2) {
43616
- throw new DeployAbort({
43617
- code: "TEMPLATE_NOT_FOUND",
43618
- message: `Could not load template app ${templateId}: ${err2 instanceof Error ? err2.message : "unknown error"}`,
43619
- retryable: true
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
- spin?.error("Template clone failed");
43704
- throw new DeployAbort({
43705
- code: "TEMPLATE_CLONE_FAILED",
43706
- message: `${err2 instanceof Error ? err2.message : "Failed to clone the template source"}
43707
-
43708
- The app ${config.app_id} is already registered \u2014 re-run \`whop apps deploy --template ${templateId} --app ${config.app_id}\` to retry.`,
43709
- retryable: true
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("logs", {
43824
- description: "Read the app's server runtime logs \u2014 console output, uncaught exceptions, failed requests (kept 7 days)",
43825
- hint: "Newest first in the structured output; the terminal view prints oldest to newest. Deploy, hit your app, then read its logs to debug server code.",
43826
- options: external_exports.object({
43827
- app: external_exports.string().optional().describe("App id (defaults to the project's linked app)"),
43828
- level: external_exports.enum(["log", "debug", "info", "warn", "error"]).optional().describe("Only console lines of this level"),
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: "Recent logs for the linked app" },
44441
+ { description: "Scaffold a new app interactively" },
43840
44442
  {
43841
- options: { level: "error", since: "1h" },
43842
- description: "Errors from the last hour"
44443
+ options: { name: "My Site", route: "my-site" },
44444
+ description: "Scaffold without prompts (agents: always pass --name)"
43843
44445
  },
43844
44446
  {
43845
- options: { query: "checkout", app: "app_xxxxxxxx" },
43846
- description: "Search another app's logs by message text"
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
- const appId = resolveLinkedAppId(c2.options.app);
43852
- const page = await listAppLogs(appId, {
43853
- level: c2.options.level,
43854
- query: c2.options.query,
43855
- app_build_id: c2.options.build,
43856
- created_after: parseTimeOption(c2.options.since),
43857
- created_before: parseTimeOption(c2.options.until),
43858
- first: c2.options.first,
43859
- after: c2.options.after
43860
- });
43861
- const logs = page.data ?? [];
43862
- if (!c2.agent && !c2.formatExplicit) {
43863
- if (logs.length === 0) {
43864
- console.log(chalk.dim("No logs in this window."));
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
- for (const row of [...logs].reverse()) printLogLine(row);
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 ? resolve6(c2.options.dir) : findProjectDir();
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 deploy\` to link this project to a Whop app.`,
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 deploy",
43888
- description: "Link this project to a Whop app and ship it"
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
- chalk.dim(
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
- chalk.dim(
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
- chalk.yellow(
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
- chalk.yellow(
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
- chalk.dim(
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
- chalk.yellow(
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, chalk.dim(`Starting dev server for ${config.name}...`));
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: "The one-stop deploy: verifies the project is a Whop-ready Vite app (offering to scaffold or link one if not), builds, typechecks, uploads the build, and promotes it to production. Use --skip_promote to upload a preview-only build, then promote it later with `whop apps builds promote <build_id>`.",
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
- template: external_exports.string().optional().describe(
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: { template: "app_xxxxxxxx" },
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
- const pm = ensurePackageManager();
44006
- let projectDir;
44007
- let freshlyScaffolded;
44008
- let config;
44009
- if (c2.options.template) {
44010
- ({ projectDir, config } = await scaffoldFromTemplate(
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 (freshlyScaffolded || !existsSync5(join7(projectDir, "node_modules", "@whop", "cli"))) {
44708
+ if (!existsSync7(join10(projectDir, "node_modules", "@whop", "cli"))) {
44041
44709
  runInstall(c2, projectDir);
44042
44710
  }
44043
- log2(c2, chalk.dim("[1/4] Building..."));
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, chalk.dim("[2/4] Typechecking..."));
44714
+ log2(c2, chalk2.dim("[2/4] Typechecking..."));
44047
44715
  runScript(projectDir, "typecheck");
44048
44716
  }
44049
44717
  }
44050
- const archivePath = join7(projectDir, BUILD_ARCHIVE);
44051
- if (!existsSync5(archivePath)) {
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 = readFileSync6(archivePath);
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
- chalk.yellow(
44732
+ chalk2.yellow(
44065
44733
  `Skipping source archive upload: ${sourceArchive.skipped}`
44066
44734
  )
44067
44735
  );
44068
44736
  } else if (sourceArchive.note) {
44069
- log2(c2, chalk.dim(`Source archive: ${sourceArchive.note}`));
44737
+ log2(c2, chalk2.dim(`Source archive: ${sourceArchive.note}`));
44070
44738
  }
44071
- const spin = c2.agent ? null : spinner2();
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 (!c2.options.skip_promote) {
44106
- const promoteSpin = c2.agent ? null : spinner2();
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, chalk.dim("[4/4] Skipping promotion (--skip_promote)"));
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
- chalk.green.bold(
44805
+ chalk2.green.bold(
44129
44806
  promoted ? "\u2713 Deployed to production" : "\u2713 Build uploaded"
44130
44807
  ),
44131
- ` ${chalk.dim(build.id)}`,
44808
+ ` ${chalk2.dim(build.id)}`,
44132
44809
  "",
44133
44810
  ...promoted && productionUrl ? [
44134
- ` ${chalk.bold("Live")} ${chalk.cyan.underline(productionUrl)}`
44811
+ ` ${chalk2.bold("Live")} ${chalk2.cyan.underline(productionUrl)}`
44135
44812
  ] : [
44136
- `${chalk.bold("Promote when ready")} ${chalk.cyan.bold(`whop apps builds promote ${build.id}`)}`
44813
+ `${chalk2.bold("Promote when ready")} ${chalk2.cyan.bold(`whop apps builds promote ${build.id}`)}`
44137
44814
  ],
44138
- ...freshlyScaffolded ? [
44815
+ ...freshInit ? [
44139
44816
  "",
44140
- chalk.bold("Next"),
44817
+ chalk2.bold("Next"),
44141
44818
  ...cdTarget !== "." ? [` cd ${cdTarget}`] : [],
44142
- ` ${chalk.cyan.bold("whop apps dev")} ${chalk.dim("\u2192 local dev server")}`,
44143
- ` ${chalk.cyan.bold("whop apps deploy")} ${chalk.dim("\u2192 ship your changes")}`
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 DeployAbort) return c2.error(err2.options);
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 spinner3 } from "@clack/prompts";
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 ? spinner3() : null;
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((resolve7) => setTimeout(resolve7, ms));
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 chalk3 from "chalk";
45778
+ import chalk4 from "chalk";
45176
45779
 
45177
45780
  // src/lib/colors.ts
45178
- import chalk2 from "chalk";
45781
+ import chalk3 from "chalk";
45179
45782
  var c = {
45180
- heading: chalk2.bold,
45181
- command: chalk2.bold,
45182
- description: chalk2.dim,
45183
- flag: chalk2.yellow,
45184
- tableHeader: chalk2.bold,
45185
- separator: chalk2.dim,
45186
- number: chalk2.dim,
45187
- muted: chalk2.dim,
45188
- id: chalk2.cyan,
45189
- link: chalk2.cyan.underline,
45190
- success: chalk2.green,
45191
- error: chalk2.red.bold
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 spinner4 = interactive ? dependencies.startSpinner("Loading your business accounts\u2026") : null;
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
- spinner4?.stop(
45913
+ spinner6?.stop(
45311
45914
  `Found ${accounts.length} business account${accounts.length === 1 ? "" : "s"}.`
45312
45915
  );
45313
45916
  } catch (error) {
45314
- spinner4?.stop("Couldn't load your business accounts.");
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
- ${chalk3.bold("You don't have a business account yet \u2014 let's create one.")}`
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 ${chalk3.bold(`"${title}"`)} ${chalk3.dim(`(${account.id})`)}`
46194
+ ${c.success("\u2713")} ${action} business account ${chalk4.bold(`"${title}"`)} ${chalk4.dim(`(${account.id})`)}`
45592
46195
  );
45593
- console.log(chalk3.dim(" Run `whop --help` to choose what to do next.\n"));
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