@revstackhq/cli 0.0.0-dev-20260227103607 → 0.0.0-dev-20260228062053

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.
Files changed (56) hide show
  1. package/.turbo/turbo-build.log +51 -42
  2. package/CHANGELOG.md +18 -0
  3. package/dist/cli.js +622 -49
  4. package/dist/cli.js.map +1 -1
  5. package/dist/commands/init.d.ts +0 -7
  6. package/dist/commands/init.js +443 -24
  7. package/dist/commands/init.js.map +1 -1
  8. package/dist/commands/login.d.ts +0 -6
  9. package/dist/commands/login.js.map +1 -1
  10. package/dist/commands/logout.d.ts +0 -5
  11. package/dist/commands/logout.js.map +1 -1
  12. package/dist/commands/pull.d.ts +0 -7
  13. package/dist/commands/pull.js +120 -11
  14. package/dist/commands/pull.js.map +1 -1
  15. package/dist/commands/push.js +59 -14
  16. package/dist/commands/push.js.map +1 -1
  17. package/dist/commands/templates/ai-agent-platform.d.ts +5 -0
  18. package/dist/commands/templates/ai-agent-platform.js +119 -0
  19. package/dist/commands/templates/ai-agent-platform.js.map +1 -0
  20. package/dist/commands/templates/b2b-saas.js +23 -10
  21. package/dist/commands/templates/b2b-saas.js.map +1 -1
  22. package/dist/commands/templates/developer-tools.d.ts +5 -0
  23. package/dist/commands/templates/developer-tools.js +131 -0
  24. package/dist/commands/templates/developer-tools.js.map +1 -0
  25. package/dist/commands/templates/ecommerce-platform.d.ts +5 -0
  26. package/dist/commands/templates/ecommerce-platform.js +144 -0
  27. package/dist/commands/templates/ecommerce-platform.js.map +1 -0
  28. package/dist/commands/templates/index.js +437 -23
  29. package/dist/commands/templates/index.js.map +1 -1
  30. package/dist/commands/templates/starter.d.ts +1 -0
  31. package/dist/commands/templates/starter.js +13 -8
  32. package/dist/commands/templates/starter.js.map +1 -1
  33. package/dist/commands/templates/usage-based.js +18 -4
  34. package/dist/commands/templates/usage-based.js.map +1 -1
  35. package/dist/utils/auth.d.ts +0 -5
  36. package/dist/utils/auth.js.map +1 -1
  37. package/dist/utils/config-loader.d.ts +0 -6
  38. package/dist/utils/config-loader.js +4 -0
  39. package/dist/utils/config-loader.js.map +1 -1
  40. package/package.json +2 -2
  41. package/src/commands/init.ts +6 -8
  42. package/src/commands/login.ts +0 -6
  43. package/src/commands/logout.ts +0 -5
  44. package/src/commands/pull.ts +148 -60
  45. package/src/commands/push.ts +67 -16
  46. package/src/commands/templates/ai-agent-platform.ts +114 -0
  47. package/src/commands/templates/b2b-saas.ts +23 -10
  48. package/src/commands/templates/developer-tools.ts +126 -0
  49. package/src/commands/templates/ecommerce-platform.ts +139 -0
  50. package/src/commands/templates/index.ts +6 -0
  51. package/src/commands/templates/starter.ts +14 -8
  52. package/src/commands/templates/usage-based.ts +18 -4
  53. package/src/utils/auth.ts +0 -6
  54. package/src/utils/config-loader.ts +6 -7
  55. package/tests/integration/pull.test.ts +6 -0
  56. package/tests/integration/push.test.ts +3 -0
package/dist/cli.js CHANGED
@@ -107,9 +107,9 @@ export const addons = {
107
107
  name: "5 Extra Seats",
108
108
  description: "Add 5 more team members to your workspace.",
109
109
  type: "recurring",
110
- prices: [
111
- { amount: 1500, currency: "USD", billing_interval: "monthly" }
112
- ],
110
+ amount: 1500,
111
+ currency: "USD",
112
+ billing_interval: "monthly",
113
113
  features: {
114
114
  seats: { value_limit: 5, type: "increment", is_hard_limit: false },
115
115
  }
@@ -118,9 +118,9 @@ export const addons = {
118
118
  name: "Priority Support",
119
119
  description: "24/7 Slack channel support.",
120
120
  type: "recurring",
121
- prices: [
122
- { amount: 9900, currency: "USD", billing_interval: "monthly" }
123
- ],
121
+ amount: 9900,
122
+ currency: "USD",
123
+ billing_interval: "monthly",
124
124
  features: {
125
125
  priority_support: { has_access: true },
126
126
  }
@@ -146,25 +146,30 @@ export const plans = {
146
146
  is_default: false,
147
147
  is_public: true,
148
148
  type: "paid",
149
- available_addons: ["extra_seats", "vip_support"],
150
149
  prices: [
151
- { amount: 2900, currency: "USD", billing_interval: "monthly", trial_period_days: 14 }
150
+ { amount: 2900, currency: "USD", billing_interval: "monthly", trial_period_days: 14, available_addons: ["extra_seats", "vip_support"] }
152
151
  ],
153
152
  features: {
154
153
  seats: { value_limit: 5, is_hard_limit: true },
155
154
  },
156
155
  }),
157
156
  };
157
+ `,
158
+ coupons: `import type { DiscountDef } from "@revstackhq/core";
159
+
160
+ export const coupons: DiscountDef[] = [];
158
161
  `,
159
162
  index: `import { defineConfig } from "@revstackhq/core";
160
163
  import { features } from "./features";
161
164
  import { addons } from "./addons";
162
165
  import { plans } from "./plans";
166
+ import { coupons } from "./coupons";
163
167
 
164
168
  export default defineConfig({
165
169
  features,
166
170
  addons,
167
171
  plans,
172
+ coupons,
168
173
  });
169
174
  `,
170
175
  root: `import config from "./revstack";
@@ -191,9 +196,9 @@ export const addons = {
191
196
  name: "10 Extra Users",
192
197
  description: "Add 10 more active users to your workspace.",
193
198
  type: "recurring",
194
- prices: [
195
- { amount: 5000, currency: "USD", billing_interval: "monthly" }
196
- ],
199
+ amount: 5000,
200
+ currency: "USD",
201
+ billing_interval: "monthly",
197
202
  features: {
198
203
  active_users: { value_limit: 10, type: "increment", is_hard_limit: true },
199
204
  }
@@ -202,12 +207,25 @@ export const addons = {
202
207
  name: "Dedicated Support",
203
208
  description: "Enterprise SLA with 1-hour response time.",
204
209
  type: "recurring",
205
- prices: [
206
- { amount: 49900, currency: "USD", billing_interval: "monthly" }
207
- ],
210
+ amount: 49900,
211
+ currency: "USD",
212
+ billing_interval: "monthly",
208
213
  features: {}
209
214
  })
210
215
  };
216
+ `,
217
+ coupons: `import type { DiscountDef } from "@revstackhq/core";
218
+
219
+ export const coupons: DiscountDef[] = [
220
+ {
221
+ code: "ENTERPRISE_B2B",
222
+ name: "Annual Contract Rebate",
223
+ type: "amount",
224
+ value: 50000,
225
+ duration: "once",
226
+ applies_to_plans: ["enterprise"]
227
+ }
228
+ ];
211
229
  `,
212
230
  plans: `import { definePlan } from "@revstackhq/core";
213
231
  import { features } from "./features";
@@ -227,9 +245,8 @@ export const plans = {
227
245
  is_default: false,
228
246
  is_public: true,
229
247
  type: "paid",
230
- available_addons: ["extra_users"],
231
248
  prices: [
232
- { amount: 9900, currency: "USD", billing_interval: "monthly" }
249
+ { amount: 9900, currency: "USD", billing_interval: "monthly", available_addons: ["extra_users"] }
233
250
  ],
234
251
  features: {
235
252
  active_users: { value_limit: 10, is_hard_limit: true },
@@ -243,9 +260,8 @@ export const plans = {
243
260
  is_default: false,
244
261
  is_public: true,
245
262
  type: "paid",
246
- available_addons: ["extra_users", "dedicated_support"],
247
263
  prices: [
248
- { amount: 49900, currency: "USD", billing_interval: "monthly" }
264
+ { amount: 49900, currency: "USD", billing_interval: "monthly", available_addons: ["extra_users", "dedicated_support"] }
249
265
  ],
250
266
  features: {
251
267
  active_users: { value_limit: 100, is_hard_limit: false },
@@ -259,11 +275,13 @@ export const plans = {
259
275
  import { features } from "./features";
260
276
  import { addons } from "./addons";
261
277
  import { plans } from "./plans";
278
+ import { coupons } from "./coupons";
262
279
 
263
280
  export default defineConfig({
264
281
  features,
265
282
  addons,
266
283
  plans,
284
+ coupons,
267
285
  });
268
286
  `,
269
287
  root: `import config from "./revstack";
@@ -289,12 +307,24 @@ export const addons = {
289
307
  name: "Premium Support",
290
308
  description: "24/7 dedicated support.",
291
309
  type: "recurring",
292
- prices: [
293
- { amount: 20000, currency: "USD", billing_interval: "monthly" }
294
- ],
310
+ amount: 20000,
311
+ currency: "USD",
312
+ billing_interval: "monthly",
295
313
  features: {}
296
314
  })
297
315
  };
316
+ `,
317
+ coupons: `import type { DiscountDef } from "@revstackhq/core";
318
+
319
+ export const coupons: DiscountDef[] = [
320
+ {
321
+ code: "STARTUP_ACCELERATOR",
322
+ name: "Startup Program Credits",
323
+ type: "amount",
324
+ value: 500000, // $5,000 credit
325
+ duration: "forever",
326
+ }
327
+ ];
298
328
  `,
299
329
  plans: `import { definePlan } from "@revstackhq/core";
300
330
  import { features } from "./features";
@@ -314,12 +344,12 @@ export const plans = {
314
344
  is_default: false,
315
345
  is_public: true,
316
346
  type: "paid",
317
- available_addons: ["premium_support"],
318
347
  prices: [
319
348
  {
320
349
  amount: 0,
321
350
  currency: "USD",
322
351
  billing_interval: "monthly",
352
+ available_addons: ["premium_support"],
323
353
  overage_configuration: {
324
354
  api_requests: { overage_amount: 15, overage_unit: 1000 }, // $0.15 per 1k extra requests
325
355
  storage_gb: { overage_amount: 50, overage_unit: 1 }, // $0.50 per extra GB
@@ -337,11 +367,392 @@ export const plans = {
337
367
  import { features } from "./features";
338
368
  import { addons } from "./addons";
339
369
  import { plans } from "./plans";
370
+ import { coupons } from "./coupons";
371
+
372
+ export default defineConfig({
373
+ features,
374
+ addons,
375
+ plans,
376
+ coupons,
377
+ });
378
+ `,
379
+ root: `import config from "./revstack";
380
+
381
+ export default config;
382
+ `
383
+ };
384
+
385
+ // src/commands/templates/ecommerce-platform.ts
386
+ var ecommercePlatform = {
387
+ features: `import { defineFeature } from "@revstackhq/core";
388
+
389
+ export const features = {
390
+ orders: defineFeature({ name: "Monthly Orders", type: "metered", unit_type: "count" }),
391
+ storefronts: defineFeature({ name: "Storefronts", type: "static", unit_type: "count" }),
392
+ advanced_analytics: defineFeature({ name: "Advanced Analytics", type: "boolean", unit_type: "custom" }),
393
+ };
394
+ `,
395
+ addons: `import { defineAddon } from "@revstackhq/core";
396
+ import { features } from "./features";
397
+
398
+ export const addons = {
399
+ extra_storefront: defineAddon<typeof features>({
400
+ name: "Additional Storefront",
401
+ description: "Launch a new brand under the same account.",
402
+ type: "recurring",
403
+ amount: 5000,
404
+ currency: "USD",
405
+ billing_interval: "monthly",
406
+ features: {
407
+ storefronts: { value_limit: 1, type: "increment", is_hard_limit: true },
408
+ }
409
+ }),
410
+ custom_domain_ssl: defineAddon<typeof features>({
411
+ name: "Custom Domain & SSL",
412
+ description: "Secure your storefront with a custom domain.",
413
+ type: "recurring",
414
+ amount: 1500,
415
+ currency: "USD",
416
+ billing_interval: "monthly",
417
+ features: {}
418
+ })
419
+ };
420
+ `,
421
+ plans: `import { definePlan } from "@revstackhq/core";
422
+ import { features } from "./features";
423
+
424
+ export const plans = {
425
+ default: definePlan<typeof features>({
426
+ name: "Default Sandbox",
427
+ description: "Test your store safely before going live.",
428
+ is_default: true,
429
+ is_public: false,
430
+ type: "free",
431
+ features: {},
432
+ }),
433
+ basic: definePlan<typeof features>({
434
+ name: "Basic Commerce",
435
+ description: "Everything you need to sell online.",
436
+ is_default: false,
437
+ is_public: true,
438
+ type: "paid",
439
+ prices: [
440
+ {
441
+ amount: 2900,
442
+ currency: "USD",
443
+ billing_interval: "monthly",
444
+ available_addons: ["custom_domain_ssl"],
445
+ overage_configuration: {
446
+ orders: { overage_amount: 50, overage_unit: 100 } // $0.50 per 100 extra orders
447
+ }
448
+ }
449
+ ],
450
+ features: {
451
+ orders: { value_limit: 500, is_hard_limit: false, reset_period: "monthly" },
452
+ storefronts: { value_limit: 1, is_hard_limit: true },
453
+ advanced_analytics: { value_bool: false },
454
+ },
455
+ }),
456
+ pro: definePlan<typeof features>({
457
+ name: "Pro Seller",
458
+ description: "For scaling businesses.",
459
+ is_default: false,
460
+ is_public: true,
461
+ type: "paid",
462
+ prices: [
463
+ {
464
+ amount: 19900,
465
+ currency: "USD",
466
+ billing_interval: "monthly",
467
+ available_addons: ["extra_storefront", "custom_domain_ssl"],
468
+ overage_configuration: {
469
+ orders: { overage_amount: 30, overage_unit: 100 } // $0.30 per 100 extra orders
470
+ }
471
+ }
472
+ ],
473
+ features: {
474
+ orders: { value_limit: 5000, is_hard_limit: false, reset_period: "monthly" },
475
+ storefronts: { value_limit: 2, is_hard_limit: true },
476
+ advanced_analytics: { value_bool: true },
477
+ },
478
+ }),
479
+ };
480
+ `,
481
+ coupons: `import type { DiscountDef } from "@revstackhq/core";
482
+
483
+ export const coupons: DiscountDef[] = [
484
+ {
485
+ code: "BFCM_PROMO",
486
+ name: "Black Friday Cyber Monday 20%",
487
+ type: "percent",
488
+ value: 20,
489
+ duration: "repeating",
490
+ duration_in_months: 6,
491
+ expires_at: "2024-11-29T00:00:00Z", // Ephemeral timestamp
492
+ max_redemptions: 5000
493
+ },
494
+ {
495
+ code: "FIRST_MONTH_FREE",
496
+ name: "New Store Launch",
497
+ type: "percent",
498
+ value: 100,
499
+ duration: "repeating",
500
+ duration_in_months: 1,
501
+ applies_to_plans: ["basic"]
502
+ }
503
+ ];
504
+ `,
505
+ index: `import { defineConfig } from "@revstackhq/core";
506
+ import { features } from "./features";
507
+ import { addons } from "./addons";
508
+ import { plans } from "./plans";
509
+ import { coupons } from "./coupons";
510
+
511
+ export default defineConfig({
512
+ features,
513
+ addons,
514
+ plans,
515
+ coupons,
516
+ });
517
+ `,
518
+ root: `import config from "./revstack";
519
+
520
+ export default config;
521
+ `
522
+ };
523
+
524
+ // src/commands/templates/developer-tools.ts
525
+ var developerTools = {
526
+ features: `import { defineFeature } from "@revstackhq/core";
527
+
528
+ export const features = {
529
+ bandwidth_gb: defineFeature({ name: "Bandwidth (GB)", type: "metered", unit_type: "custom" }),
530
+ compute_hours: defineFeature({ name: "Compute (GB-Hours)", type: "metered", unit_type: "custom" }),
531
+ serverless_invokes: defineFeature({ name: "Serverless Invocations", type: "metered", unit_type: "count" }),
532
+ sso_auth: defineFeature({ name: "Enterprise SSO", type: "boolean", unit_type: "custom" })
533
+ };
534
+ `,
535
+ addons: `import { defineAddon } from "@revstackhq/core";
536
+ import { features } from "./features";
537
+
538
+ export const addons = {
539
+ dedicated_ipv4: defineAddon<typeof features>({
540
+ name: "Dedicated IPv4 Address",
541
+ description: "Assign a static IP to your deployments.",
542
+ type: "recurring",
543
+ amount: 500,
544
+ currency: "USD",
545
+ billing_interval: "monthly",
546
+ features: {}
547
+ }),
548
+ premium_sla: defineAddon<typeof features>({
549
+ name: "Premium Escalation SLA",
550
+ description: "Direct engineering support with 30-min response time.",
551
+ type: "recurring",
552
+ amount: 150000,
553
+ currency: "USD",
554
+ billing_interval: "monthly",
555
+ features: {}
556
+ })
557
+ };
558
+ `,
559
+ plans: `import { definePlan } from "@revstackhq/core";
560
+ import { features } from "./features";
561
+
562
+ export const plans = {
563
+ default: definePlan<typeof features>({
564
+ name: "Hobby",
565
+ description: "For personal or non-commercial projects.",
566
+ is_default: true,
567
+ is_public: true,
568
+ type: "free",
569
+ features: {
570
+ bandwidth_gb: { value_limit: 100, is_hard_limit: true, reset_period: "monthly" },
571
+ compute_hours: { value_limit: 100, is_hard_limit: true, reset_period: "monthly" },
572
+ serverless_invokes: { value_limit: 100000, is_hard_limit: true, reset_period: "monthly" },
573
+ sso_auth: { value_bool: false }
574
+ },
575
+ }),
576
+ pro: definePlan<typeof features>({
577
+ name: "Pro",
578
+ description: "For production apps and growing teams.",
579
+ is_default: false,
580
+ is_public: true,
581
+ type: "paid",
582
+ prices: [
583
+ {
584
+ amount: 2000,
585
+ currency: "USD",
586
+ billing_interval: "monthly",
587
+ available_addons: ["dedicated_ipv4"],
588
+ overage_configuration: {
589
+ bandwidth_gb: { overage_amount: 40, overage_unit: 100 }, // $0.40 per 100GB extra
590
+ compute_hours: { overage_amount: 2, overage_unit: 1 }, // $0.02 per extra GB hr
591
+ serverless_invokes: { overage_amount: 50, overage_unit: 1000000 } // $0.50 per 1M invokes
592
+ }
593
+ }
594
+ ],
595
+ features: {
596
+ bandwidth_gb: { value_limit: 1000, is_hard_limit: false, reset_period: "monthly" },
597
+ compute_hours: { value_limit: 1000, is_hard_limit: false, reset_period: "monthly" },
598
+ serverless_invokes: { value_limit: 5000000, is_hard_limit: false, reset_period: "monthly" },
599
+ sso_auth: { value_bool: false }
600
+ },
601
+ }),
602
+ enterprise: definePlan<typeof features>({
603
+ name: "Enterprise",
604
+ description: "Custom infrastructure and legal compliance.",
605
+ is_default: false,
606
+ is_public: true,
607
+ type: "custom",
608
+ prices: [],
609
+ features: {
610
+ bandwidth_gb: { value_limit: 50000, is_hard_limit: false, reset_period: "monthly" },
611
+ compute_hours: { value_limit: 10000, is_hard_limit: false, reset_period: "monthly" },
612
+ serverless_invokes: { value_limit: 100000000, is_hard_limit: false, reset_period: "monthly" },
613
+ sso_auth: { value_bool: true }
614
+ },
615
+ }),
616
+ };
617
+ `,
618
+ coupons: `import type { DiscountDef } from "@revstackhq/core";
619
+
620
+ export const coupons: DiscountDef[] = [
621
+ {
622
+ code: "YCOMBINATOR",
623
+ name: "YC Startup Accelerator Credit",
624
+ type: "amount",
625
+ value: 1000000, // $10,000 credit
626
+ duration: "forever",
627
+ applies_to_plans: ["pro", "enterprise"]
628
+ }
629
+ ];
630
+ `,
631
+ index: `import { defineConfig } from "@revstackhq/core";
632
+ import { features } from "./features";
633
+ import { addons } from "./addons";
634
+ import { plans } from "./plans";
635
+ import { coupons } from "./coupons";
340
636
 
341
637
  export default defineConfig({
342
638
  features,
343
639
  addons,
344
640
  plans,
641
+ coupons,
642
+ });
643
+ `,
644
+ root: `import config from "./revstack";
645
+
646
+ export default config;
647
+ `
648
+ };
649
+
650
+ // src/commands/templates/ai-agent-platform.ts
651
+ var aiAgentPlatform = {
652
+ features: `import { defineFeature } from "@revstackhq/core";
653
+
654
+ export const features = {
655
+ llm_tokens: defineFeature({ name: "LLM Tokens (Input+Output)", type: "metered", unit_type: "count" }),
656
+ active_agents: defineFeature({ name: "Concurrent Agents", type: "static", unit_type: "count" }),
657
+ vector_storage_gb: defineFeature({ name: "Vector Database (GB)", type: "metered", unit_type: "custom" }),
658
+ custom_fine_tuning: defineFeature({ name: "Model Fine-Tuning", type: "boolean", unit_type: "custom" })
659
+ };
660
+ `,
661
+ addons: `import { defineAddon } from "@revstackhq/core";
662
+ import { features } from "./features";
663
+
664
+ export const addons = {
665
+ extra_vector_storage: defineAddon<typeof features>({
666
+ name: "10GB Vector Storage Block",
667
+ description: "Retain long-term memory for your AI agents.",
668
+ type: "recurring",
669
+ amount: 1000,
670
+ currency: "USD",
671
+ billing_interval: "monthly",
672
+ features: {
673
+ vector_storage_gb: { value_limit: 10, type: "increment", is_hard_limit: false }
674
+ }
675
+ }),
676
+ fine_tuning_job: defineAddon<typeof features>({
677
+ name: "Fine-Tuning Job Runner",
678
+ description: "Pay once to run a distributed model fine-tuning job on your dataset.",
679
+ type: "one_time",
680
+ amount: 25000,
681
+ currency: "USD",
682
+ features: {
683
+ custom_fine_tuning: { has_access: true }
684
+ }
685
+ })
686
+ };
687
+ `,
688
+ plans: `import { definePlan } from "@revstackhq/core";
689
+ import { features } from "./features";
690
+
691
+ export const plans = {
692
+ default: definePlan<typeof features>({
693
+ name: "Free Preview",
694
+ description: "Trial sandbox using shared models.",
695
+ is_default: true,
696
+ is_public: true,
697
+ type: "free",
698
+ features: {
699
+ llm_tokens: { value_limit: 100000, is_hard_limit: true, reset_period: "monthly" },
700
+ active_agents: { value_limit: 1, is_hard_limit: true },
701
+ vector_storage_gb: { value_limit: 0, is_hard_limit: true, reset_period: "never" },
702
+ custom_fine_tuning: { value_bool: false }
703
+ },
704
+ }),
705
+ builder: definePlan<typeof features>({
706
+ name: "AI Builder",
707
+ description: "High concurrency and dedicated model endpoints.",
708
+ is_default: false,
709
+ is_public: true,
710
+ type: "paid",
711
+ prices: [
712
+ {
713
+ amount: 4900,
714
+ currency: "USD",
715
+ billing_interval: "monthly",
716
+ available_addons: ["extra_vector_storage", "fine_tuning_job"],
717
+ overage_configuration: {
718
+ llm_tokens: { overage_amount: 15, overage_unit: 1000000 } // $0.15 per 1M tokens
719
+ }
720
+ }
721
+ ],
722
+ features: {
723
+ llm_tokens: { value_limit: 10000000, is_hard_limit: false, reset_period: "monthly" }, // 10M free tokens included
724
+ active_agents: { value_limit: 10, is_hard_limit: true },
725
+ vector_storage_gb: { value_limit: 5, is_hard_limit: true, reset_period: "never" }, // Hard limit until they buy the addon
726
+ custom_fine_tuning: { value_bool: false } // Only unlocked via the one-off addon
727
+ },
728
+ }),
729
+ };
730
+ `,
731
+ coupons: `import type { DiscountDef } from "@revstackhq/core";
732
+
733
+ export const coupons: DiscountDef[] = [
734
+ {
735
+ code: "BETA_TESTER",
736
+ name: "Early Beta Access Discount",
737
+ type: "percent",
738
+ value: 50,
739
+ duration: "repeating",
740
+ duration_in_months: 12,
741
+ max_redemptions: 100
742
+ }
743
+ ];
744
+ `,
745
+ index: `import { defineConfig } from "@revstackhq/core";
746
+ import { features } from "./features";
747
+ import { addons } from "./addons";
748
+ import { plans } from "./plans";
749
+ import { coupons } from "./coupons";
750
+
751
+ export default defineConfig({
752
+ features,
753
+ addons,
754
+ plans,
755
+ coupons,
345
756
  });
346
757
  `,
347
758
  root: `import config from "./revstack";
@@ -354,13 +765,16 @@ export default config;
354
765
  var TEMPLATES = {
355
766
  starter,
356
767
  "b2b-saas": b2bSaas,
357
- "usage-based": usageBased
768
+ "usage-based": usageBased,
769
+ "ecommerce-platform": ecommercePlatform,
770
+ "developer-tools": developerTools,
771
+ "ai-agent-platform": aiAgentPlatform
358
772
  };
359
773
 
360
774
  // src/commands/init.ts
361
775
  var initCommand = new Command3("init").description("Scaffold a new revstack.config.ts in the current directory").option(
362
776
  "-t, --template <name>",
363
- "Choose a starting template (starter, b2b-saas, usage-based)",
777
+ `Choose a starting template (${Object.keys(TEMPLATES).join(", ")})`,
364
778
  "starter"
365
779
  ).action(async (options) => {
366
780
  const templateName = options.template || "starter";
@@ -397,6 +811,11 @@ var initCommand = new Command3("init").description("Scaffold a new revstack.conf
397
811
  template.addons,
398
812
  "utf-8"
399
813
  );
814
+ fs2.writeFileSync(
815
+ path2.resolve(revstackDir, "coupons.ts"),
816
+ template.coupons,
817
+ "utf-8"
818
+ );
400
819
  fs2.writeFileSync(
401
820
  path2.resolve(revstackDir, "plans.ts"),
402
821
  template.plans,
@@ -495,6 +914,10 @@ async function loadLocalConfig(cwd) {
495
914
  "\n \u2716 Could not find revstack.config.ts in the current directory.\n"
496
915
  ) + chalk4.dim(" Run ") + chalk4.bold("revstack init") + chalk4.dim(" to create one.\n")
497
916
  );
917
+ } else if (err.name === "SyntaxError" || error instanceof SyntaxError) {
918
+ console.error(
919
+ chalk4.red("\n \u2716 Syntax Error in revstack.config.ts\n") + chalk4.dim(" " + (err.message ?? String(error))) + "\n"
920
+ );
498
921
  } else {
499
922
  console.error(
500
923
  chalk4.red("\n \u2716 Failed to parse revstack.config.ts\n") + chalk4.dim(" " + (err.message ?? String(error))) + "\n"
@@ -505,7 +928,11 @@ async function loadLocalConfig(cwd) {
505
928
  }
506
929
 
507
930
  // src/commands/push.ts
508
- import { validateConfig, RevstackValidationError } from "@revstackhq/core";
931
+ import {
932
+ validateConfig,
933
+ RevstackValidationError,
934
+ RevstackConfigSchema
935
+ } from "@revstackhq/core";
509
936
  var API_BASE = "https://app.revstack.dev";
510
937
  var DIFF_ICONS = {
511
938
  added: chalk5.green(" + "),
@@ -517,23 +944,46 @@ var DIFF_COLORS = {
517
944
  removed: chalk5.red,
518
945
  updated: chalk5.yellow
519
946
  };
520
- function printDiff(diff) {
947
+ function printDiff(diff, env) {
521
948
  if (diff.length === 0) {
522
949
  console.log(
523
950
  chalk5.dim("\n No changes detected. Your config is up to date.\n")
524
951
  );
525
952
  return;
526
953
  }
527
- console.log(chalk5.bold("\n Changes:\n"));
528
- for (const entry of diff) {
529
- const icon = DIFF_ICONS[entry.action];
530
- const color = DIFF_COLORS[entry.action];
531
- const label = chalk5.dim(`[${entry.entity}]`);
954
+ if (env === "production") {
532
955
  console.log(
533
- `${icon}${color(entry.id)} ${label} ${chalk5.white(entry.message)}`
956
+ chalk5.bgRed.white.bold("\n \u26A0\uFE0F YOU ARE PUSHING TO PRODUCTION \u26A0\uFE0F \n")
534
957
  );
958
+ } else {
959
+ console.log(chalk5.bold("\n Changes:\n"));
960
+ }
961
+ const groups = {};
962
+ for (const entry of diff) {
963
+ if (!groups[entry.entity]) groups[entry.entity] = [];
964
+ groups[entry.entity].push(entry);
965
+ }
966
+ let added = 0;
967
+ let updated = 0;
968
+ let removed = 0;
969
+ for (const [entityName, entries] of Object.entries(groups)) {
970
+ console.log(chalk5.dim(` ${entityName}s`));
971
+ for (const entry of entries) {
972
+ if (entry.action === "added") added++;
973
+ if (entry.action === "updated") updated++;
974
+ if (entry.action === "removed") removed++;
975
+ const icon = DIFF_ICONS[entry.action];
976
+ const color = DIFF_COLORS[entry.action];
977
+ console.log(`${icon}${color(entry.id)} ${chalk5.white(entry.message)}`);
978
+ }
979
+ console.log();
535
980
  }
536
- console.log();
981
+ console.log(
982
+ chalk5.bold(
983
+ ` Summary: ${added} added, ${updated} updated, ${removed} removed
984
+ `
985
+ )
986
+ );
537
987
  }
538
988
  function requireAuth() {
539
989
  const apiKey = getApiKey();
@@ -553,11 +1003,25 @@ var pushCommand = new Command4("push").description("Push your local billing conf
553
1003
  prefixText: " "
554
1004
  }).start();
555
1005
  try {
556
- validateConfig(config);
1006
+ const parsedConfig = RevstackConfigSchema.parse(config);
1007
+ validateConfig(parsedConfig);
557
1008
  validationSpinner.succeed("Configuration validated");
558
1009
  } catch (error) {
1010
+ validationSpinner.fail("Configuration invalid");
1011
+ if (error?.name === "ZodError") {
1012
+ console.error(
1013
+ chalk5.red(
1014
+ "\n \u2716 The billing configuration contains schema/formatting errors:\n"
1015
+ )
1016
+ );
1017
+ for (const err of error.errors || []) {
1018
+ const path5 = err.path.join(".");
1019
+ console.error(chalk5.red(` \u2022 [${path5}] ${err.message}`));
1020
+ }
1021
+ console.log();
1022
+ process.exit(1);
1023
+ }
559
1024
  if (error instanceof RevstackValidationError || error?.name === "RevstackValidationError") {
560
- validationSpinner.fail("Configuration invalid");
561
1025
  console.error(
562
1026
  chalk5.red(
563
1027
  "\n \u2716 The billing configuration contains business logic errors:\n"
@@ -611,14 +1075,14 @@ var pushCommand = new Command4("push").description("Push your local billing conf
611
1075
  `));
612
1076
  process.exit(1);
613
1077
  }
614
- printDiff(diffResponse.diff);
1078
+ printDiff(diffResponse.diff, options.env);
615
1079
  if (diffResponse.diff.length === 0) {
616
1080
  return;
617
1081
  }
618
1082
  if (!diffResponse.canPush) {
619
1083
  console.log(
620
- chalk5.red(" \u2716 Push is blocked.\n") + chalk5.dim(
621
- ` ${diffResponse.blockedReason ?? "The server rejected this configuration."}
1084
+ "\n" + chalk5.bgRed.white.bold(" BLOCKED: PUSH IMPOSSIBLE ") + "\n\n" + chalk5.red(
1085
+ ` \u2716 ${diffResponse.blockedReason ?? "The server rejected this configuration due to destructive changes."}
622
1086
  `
623
1087
  )
624
1088
  );
@@ -677,6 +1141,8 @@ import ora3 from "ora";
677
1141
  import prompts3 from "prompts";
678
1142
  import fs3 from "fs";
679
1143
  import path4 from "path";
1144
+ import { execa } from "execa";
1145
+ import { RevstackConfigSchema as RevstackConfigSchema2 } from "@revstackhq/core";
680
1146
  function serializeObject(obj, depth = 0) {
681
1147
  const entries = Object.entries(obj);
682
1148
  if (entries.length === 0) return "{}";
@@ -747,15 +1213,83 @@ ${planEntries.join("\n")}
747
1213
  };
748
1214
  `;
749
1215
  }
750
- function generateRootConfigSource() {
751
- return `import { defineConfig } from "@revstackhq/core";
752
- import { features } from "./revstack/features";
753
- import { plans } from "./revstack/plans";
1216
+ function generateAddonsSource(config) {
1217
+ if (!config.addons) return "";
1218
+ const addonEntries = Object.entries(config.addons).map(([slug, addon]) => {
1219
+ const props = {
1220
+ name: addon.name,
1221
+ type: addon.type,
1222
+ amount: addon.amount,
1223
+ currency: addon.currency
1224
+ };
1225
+ if (addon.description) props.description = addon.description;
1226
+ if (addon.billing_interval) props.billing_interval = addon.billing_interval;
1227
+ props.features = addon.features;
1228
+ return ` ${slug}: defineAddon<typeof features>(${serializeObject(props, 2)}),`;
1229
+ });
1230
+ return `import { defineAddon } from "@revstackhq/core";
1231
+ import { features } from "./features";
754
1232
 
755
- export default defineConfig({
756
- features,
757
- plans,
758
- });
1233
+ export const addons = {
1234
+ ${addonEntries.join("\n")}
1235
+ };
1236
+ `;
1237
+ }
1238
+ function generateCouponsSource(config) {
1239
+ if (!config.coupons || config.coupons.length === 0) return "";
1240
+ const couponsArray = config.coupons.map((coupon) => {
1241
+ const props = {
1242
+ code: coupon.code,
1243
+ type: coupon.type,
1244
+ value: coupon.value,
1245
+ duration: coupon.duration
1246
+ };
1247
+ if (coupon.name) props.name = coupon.name;
1248
+ if (coupon.duration_in_months)
1249
+ props.duration_in_months = coupon.duration_in_months;
1250
+ if (coupon.applies_to_plans)
1251
+ props.applies_to_plans = coupon.applies_to_plans;
1252
+ if (coupon.max_redemptions) props.max_redemptions = coupon.max_redemptions;
1253
+ if (coupon.expires_at) props.expires_at = coupon.expires_at;
1254
+ return props;
1255
+ });
1256
+ return `import type { DiscountDef } from "@revstackhq/core";
1257
+
1258
+ export const coupons: DiscountDef[] = ${serializeArray(couponsArray, 0)};
1259
+ `;
1260
+ }
1261
+ function generateRootConfigSource(config) {
1262
+ const hasAddons = !!config.addons && Object.keys(config.addons).length > 0;
1263
+ const hasCoupons = !!config.coupons && config.coupons.length > 0;
1264
+ const imports = [
1265
+ `import { defineConfig } from "@revstackhq/core";`,
1266
+ `import { features } from "./revstack/features";`,
1267
+ `import { plans } from "./revstack/plans";`
1268
+ ];
1269
+ if (hasAddons) {
1270
+ imports.push(`import { addons } from "./revstack/addons";`);
1271
+ }
1272
+ if (hasCoupons) {
1273
+ imports.push(`import { coupons } from "./revstack/coupons";`);
1274
+ }
1275
+ const configProps = {
1276
+ features: "__RAW_features",
1277
+ plans: "__RAW_plans"
1278
+ };
1279
+ if (hasAddons) {
1280
+ configProps.addons = "__RAW_addons";
1281
+ }
1282
+ if (hasCoupons) {
1283
+ configProps.coupons = "__RAW_coupons";
1284
+ }
1285
+ let configStr = serializeObject(configProps, 0);
1286
+ configStr = configStr.replace(/"__RAW_features"/g, "features");
1287
+ configStr = configStr.replace(/"__RAW_plans"/g, "plans");
1288
+ configStr = configStr.replace(/"__RAW_addons"/g, "addons");
1289
+ configStr = configStr.replace(/"__RAW_coupons"/g, "coupons");
1290
+ return `${imports.join("\n")}
1291
+
1292
+ export default defineConfig(${configStr});
759
1293
  `;
760
1294
  }
761
1295
  var API_BASE2 = "https://app.revstack.dev";
@@ -790,7 +1324,16 @@ var pullCommand = new Command5("pull").description(
790
1324
  );
791
1325
  process.exit(1);
792
1326
  }
793
- remoteConfig = await res.json();
1327
+ const rawData = await res.json();
1328
+ try {
1329
+ remoteConfig = RevstackConfigSchema2.parse(rawData);
1330
+ } catch (validationError) {
1331
+ spinner.fail("Remote config failed schema validation");
1332
+ console.error(chalk6.red(`
1333
+ ${validationError.message}
1334
+ `));
1335
+ process.exit(1);
1336
+ }
794
1337
  spinner.succeed("Remote config fetched");
795
1338
  } catch (error) {
796
1339
  spinner.fail("Failed to reach Revstack Cloud");
@@ -801,8 +1344,12 @@ var pullCommand = new Command5("pull").description(
801
1344
  }
802
1345
  const featureCount = Object.keys(remoteConfig.features).length;
803
1346
  const planCount = Object.keys(remoteConfig.plans).length;
1347
+ const addonCount = remoteConfig.addons ? Object.keys(remoteConfig.addons).length : 0;
1348
+ const couponCount = remoteConfig.coupons ? remoteConfig.coupons.length : 0;
804
1349
  console.log(
805
- "\n" + chalk6.dim(" Remote state: ") + chalk6.white(`${featureCount} features, ${planCount} plans`) + chalk6.dim(` (${options.env})
1350
+ "\n" + chalk6.dim(" Remote state: ") + chalk6.white(
1351
+ `${featureCount} features, ${planCount} plans, ${addonCount} addons, ${couponCount} coupons`
1352
+ ) + chalk6.dim(` (${options.env})
806
1353
  `)
807
1354
  );
808
1355
  const cwd = process.cwd();
@@ -829,10 +1376,36 @@ var pullCommand = new Command5("pull").description(
829
1376
  }
830
1377
  const featuresSource = generateFeaturesSource(remoteConfig);
831
1378
  const plansSource = generatePlansSource(remoteConfig);
832
- const rootSource = generateRootConfigSource();
1379
+ const rootSource = generateRootConfigSource(remoteConfig);
833
1380
  fs3.writeFileSync(featuresPath, featuresSource, "utf-8");
834
1381
  fs3.writeFileSync(plansPath, plansSource, "utf-8");
835
1382
  fs3.writeFileSync(configPath, rootSource, "utf-8");
1383
+ if (remoteConfig.addons && Object.keys(remoteConfig.addons).length > 0) {
1384
+ const addonsSource = generateAddonsSource(remoteConfig);
1385
+ const addonsPath = path4.resolve(revstackDir, "addons.ts");
1386
+ fs3.writeFileSync(addonsPath, addonsSource, "utf-8");
1387
+ }
1388
+ if (remoteConfig.coupons && remoteConfig.coupons.length > 0) {
1389
+ const couponsSource = generateCouponsSource(remoteConfig);
1390
+ const couponsPath = path4.resolve(revstackDir, "coupons.ts");
1391
+ fs3.writeFileSync(couponsPath, couponsSource, "utf-8");
1392
+ }
1393
+ const formatSpinner = ora3("Formatting generated files...").start();
1394
+ try {
1395
+ await execa(
1396
+ "npx",
1397
+ ["prettier", "--write", "revstack.config.ts", "revstack/**/*.ts"],
1398
+ {
1399
+ cwd,
1400
+ stdio: "ignore"
1401
+ }
1402
+ );
1403
+ formatSpinner.succeed("Files formatted successfully");
1404
+ } catch (err) {
1405
+ formatSpinner.info(
1406
+ "Files written properly, but automatic formatting (prettier) skipped or failed."
1407
+ );
1408
+ }
836
1409
  console.log(
837
1410
  "\n" + chalk6.green(" \u2714 Local files updated from remote.\n") + chalk6.dim(" Review the files and run ") + chalk6.bold("revstack push") + chalk6.dim(" to re-deploy.\n")
838
1411
  );