@revstackhq/cli 0.0.0-dev-20260226063200 → 0.0.0-dev-20260227092523

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 (42) hide show
  1. package/.turbo/turbo-build.log +42 -30
  2. package/CHANGELOG.md +18 -0
  3. package/README.md +58 -38
  4. package/dist/cli.js +314 -43
  5. package/dist/cli.js.map +1 -1
  6. package/dist/commands/init.js +279 -42
  7. package/dist/commands/init.js.map +1 -1
  8. package/dist/commands/login.js.map +1 -1
  9. package/dist/commands/logout.js +3 -1
  10. package/dist/commands/logout.js.map +1 -1
  11. package/dist/commands/pull.js.map +1 -1
  12. package/dist/commands/push.js +32 -0
  13. package/dist/commands/push.js.map +1 -1
  14. package/dist/commands/templates/b2b-saas.d.ts +5 -0
  15. package/dist/commands/templates/b2b-saas.js +104 -0
  16. package/dist/commands/templates/b2b-saas.js.map +1 -0
  17. package/dist/commands/templates/index.d.ts +5 -0
  18. package/dist/commands/templates/index.js +264 -0
  19. package/dist/commands/templates/index.js.map +1 -0
  20. package/dist/commands/templates/starter.d.ts +10 -0
  21. package/dist/commands/templates/starter.js +88 -0
  22. package/dist/commands/templates/starter.js.map +1 -0
  23. package/dist/commands/templates/usage-based.d.ts +5 -0
  24. package/dist/commands/templates/usage-based.js +75 -0
  25. package/dist/commands/templates/usage-based.js.map +1 -0
  26. package/dist/utils/auth.js.map +1 -1
  27. package/dist/utils/config-loader.js.map +1 -1
  28. package/package.json +3 -2
  29. package/src/cli.ts +32 -32
  30. package/src/commands/init.ts +187 -210
  31. package/src/commands/login.ts +39 -39
  32. package/src/commands/logout.ts +27 -25
  33. package/src/commands/pull.ts +280 -280
  34. package/src/commands/push.ts +244 -206
  35. package/src/commands/templates/b2b-saas.ts +99 -0
  36. package/src/commands/templates/index.ts +12 -0
  37. package/src/commands/templates/starter.ts +89 -0
  38. package/src/commands/templates/usage-based.ts +70 -0
  39. package/src/utils/auth.ts +59 -59
  40. package/src/utils/config-loader.ts +57 -57
  41. package/tests/integration/init.test.ts +12 -2
  42. package/tests/integration/push.test.ts +20 -4
@@ -1,36 +1,48 @@
1
- [?9001h[?1004h[?25l> @revstackhq/cli@0.0.0-dev-20260226063200 build C:\Users\flava\OneDrive\Desktop\Work\revstack-os\packages\cli
2
- > tsup]0;C:\WINDOWS\system32\cmd.exe[?25hCLI Building entry: src/cli.ts, src/commands/init.ts, src/commands/login.ts, src/commands/logout.ts, src/commands/pull.ts, src/commands/push.ts, src/utils/auth.ts, src/utils/config-loader.ts
1
+ [?9001h[?1004h[?25l> @revstackhq/cli@0.0.0-dev-20260227092523 build C:\Users\flava\OneDrive\Desktop\Work\revstack-os\packages\cli
2
+ > tsup]0;C:\WINDOWS\system32\cmd.exe[?25hCLI Building entry: src/cli.ts, src/commands/init.ts, src/commands/login.ts, src/commands/logout.ts, src/commands/pull.ts, src/commands/push.ts, src/utils/auth.ts, src/utils/config-loader.ts, src/commands/templates/b2b-saas.ts, src/commands/templates/index.ts, src/commands/templates/starter.ts, src/commands/templates/usage-based.ts
3
3
  CLI Using tsconfig: tsconfig.json
4
4
  CLI tsup v8.5.1
5
- CLI Using tsup config: C:\Users\flava\OneDrive\Desktop\Work\revstack-os\packages\cli\tsup.config.ts
6
- CLI Target: node18
5
+ CLI Using tsup config: C:\Users\flava\OneDrive\Desktop\Work\revstack-os\packages\cli\tsup.config.ts
6
+ CLI Target: node18
7
7
  CLI Cleaning output folder
8
8
  ESM Build start
9
- ESM dist\cli.js 18.36 KB
10
- ESM dist\commands\logout.js 1.15 KB
11
- ESM dist\commands\init.js 5.14 KB
12
- ESM dist\commands\login.js 1.42 KB
13
- ESM dist\utils\auth.js 971.00 B
14
- ESM dist\commands\pull.js 6.22 KB
15
- ESM dist\utils\config-loader.js 1.05 KB
16
- ESM dist\commands\push.js 5.31 KB
17
- ESM dist\cli.js.map 41.02 KB
18
- ESM dist\commands\logout.js.map 3.17 KB
19
- ESM dist\commands\init.js.map 9.92 KB
20
- ESM dist\commands\login.js.map 3.89 KB
21
- ESM dist\utils\auth.js.map 2.36 KB
22
- ESM dist\utils\config-loader.js.map 2.66 KB
23
- ESM dist\commands\pull.js.map 14.72 KB
24
- ESM dist\commands\push.js.map 13.64 KB
25
- ESM ⚡️ Build success in 101ms
9
+ ESM dist\cli.js 25.74 KB
10
+ ESM dist\commands\templates\b2b-saas.js 2.85 KB
11
+ ESM dist\commands\login.js 1.42 KB
12
+ ESM dist\commands\init.js 11.53 KB
13
+ ESM dist\utils\config-loader.js 1.05 KB
14
+ ESM dist\utils\auth.js 971.00 B
15
+ ESM dist\commands\logout.js 1.17 KB
16
+ ESM dist\commands\pull.js 6.22 KB
17
+ ESM dist\commands\push.js 6.27 KB
18
+ ESM dist\commands\templates\starter.js 2.27 KB
19
+ ESM dist\commands\templates\usage-based.js 2.02 KB
20
+ ESM dist\commands\templates\index.js 7.09 KB
21
+ ESM dist\cli.js.map 50.88 KB
22
+ ESM dist\commands\login.js.map 3.70 KB
23
+ ESM dist\commands\templates\b2b-saas.js.map 3.84 KB
24
+ ESM dist\commands\init.js.map 19.25 KB
25
+ ESM dist\utils\config-loader.js.map 2.55 KB
26
+ ESM dist\commands\logout.js.map 3.03 KB
27
+ ESM dist\utils\auth.js.map 2.24 KB
28
+ ESM dist\commands\pull.js.map 14.06 KB
29
+ ESM dist\commands\templates\usage-based.js.map 2.75 KB
30
+ ESM dist\commands\templates\starter.js.map 3.21 KB
31
+ ESM dist\commands\push.js.map 14.83 KB
32
+ ESM dist\commands\templates\index.js.map 10.15 KB
33
+ ESM ⚡️ Build success in 81ms
26
34
  DTS Build start
27
- DTS ⚡️ Build success in 4049ms
28
- DTS dist\cli.d.ts 13.00 B
29
- DTS dist\commands\init.d.ts 353.00 B
30
- DTS dist\commands\login.d.ts 288.00 B
31
- DTS dist\commands\logout.d.ts 192.00 B
32
- DTS dist\commands\pull.d.ts 334.00 B
33
- DTS dist\commands\push.d.ts 328.00 B
34
- DTS dist\utils\auth.d.ts 637.00 B
35
- DTS dist\utils\config-loader.d.ts 566.00 B
35
+ DTS ⚡️ Build success in 7779ms
36
+ DTS dist\cli.d.ts 13.00 B
37
+ DTS dist\commands\init.d.ts 353.00 B
38
+ DTS dist\commands\login.d.ts 288.00 B
39
+ DTS dist\commands\logout.d.ts 192.00 B
40
+ DTS dist\commands\pull.d.ts 334.00 B
41
+ DTS dist\commands\push.d.ts 328.00 B
42
+ DTS dist\utils\auth.d.ts 637.00 B
43
+ DTS dist\utils\config-loader.d.ts 566.00 B
44
+ DTS dist\commands\templates\b2b-saas.d.ts 108.00 B
45
+ DTS dist\commands\templates\index.d.ts 144.00 B
46
+ DTS dist\commands\templates\usage-based.d.ts 114.00 B
47
+ DTS dist\commands\templates\starter.d.ts 208.00 B
36
48
  [?9001l[?1004l
package/CHANGELOG.md CHANGED
@@ -1,5 +1,23 @@
1
1
  # @revstackhq/cli
2
2
 
3
+ ## 0.0.0-dev-20260227092523
4
+
5
+ ### Minor Changes
6
+
7
+ - implement modular enterprise configuration structure and interactive addons in init command
8
+
9
+ ## 0.0.0-dev-20260226064743
10
+
11
+ ### Patch Changes
12
+
13
+ - fix: respect snapshot releases during core package scaffold
14
+
15
+ ## 0.0.0-dev-20260226064458
16
+
17
+ ### Patch Changes
18
+
19
+ - fix: format console output line breaks correctly in init command
20
+
3
21
  ## 0.0.0-dev-20260226063200
4
22
 
5
23
  ### Patch Changes
package/README.md CHANGED
@@ -32,12 +32,14 @@ Scaffold a new `revstack.config.ts` in your project root:
32
32
  revstack init
33
33
  ```
34
34
 
35
- This creates a starter config with example plans and features using the type-safe helpers from `@revstackhq/core`:
35
+ This creates a `revstack/` directory and a `revstack.config.ts` file in your project root, scaffolding a starter config with example plans and features using type-safe helpers from `@revstackhq/core`:
36
+
37
+ **`revstack/features.ts`**
36
38
 
37
39
  ```typescript
38
- import { defineConfig, definePlan, defineFeature } from "@revstackhq/core";
40
+ import { defineFeature } from "@revstackhq/core";
39
41
 
40
- const features = {
42
+ export const features = {
41
43
  seats: defineFeature({
42
44
  name: "Seats",
43
45
  type: "static",
@@ -49,44 +51,62 @@ const features = {
49
51
  unit_type: "count",
50
52
  }),
51
53
  };
54
+ ```
55
+
56
+ **`revstack/plans.ts`**
57
+
58
+ ```typescript
59
+ import { definePlan } from "@revstackhq/core";
60
+ import { features } from "./features";
61
+
62
+ export const plans = {
63
+ // DO NOT DELETE: Automatically created default plan for guests.
64
+ default: definePlan<typeof features>({
65
+ name: "Default",
66
+ description: "Automatically created default plan for guests.",
67
+ is_default: true,
68
+ is_public: false,
69
+ type: "free",
70
+ features: {},
71
+ }),
72
+ pro: definePlan<typeof features>({
73
+ name: "Pro",
74
+ description: "For professional teams.",
75
+ is_default: false,
76
+ is_public: true,
77
+ type: "paid",
78
+ prices: [
79
+ {
80
+ amount: 2900,
81
+ currency: "USD",
82
+ billing_interval: "monthly",
83
+ trial_period_days: 14,
84
+ },
85
+ {
86
+ amount: 29000,
87
+ currency: "USD",
88
+ billing_interval: "yearly",
89
+ trial_period_days: 14,
90
+ },
91
+ ],
92
+ features: {
93
+ seats: { value_limit: 5, is_hard_limit: true },
94
+ ai_tokens: { value_limit: 1000, reset_period: "monthly" },
95
+ },
96
+ }),
97
+ };
98
+ ```
99
+
100
+ **`revstack.config.ts`**
101
+
102
+ ```typescript
103
+ import { defineConfig } from "@revstackhq/core";
104
+ import { features } from "./revstack/features";
105
+ import { plans } from "./revstack/plans";
52
106
 
53
107
  export default defineConfig({
54
108
  features,
55
- plans: {
56
- default: definePlan<typeof features>({
57
- name: "Default",
58
- description: "Automatically created default plan for guests.",
59
- is_default: true,
60
- is_public: false,
61
- type: "free",
62
- features: {},
63
- }),
64
- pro: definePlan<typeof features>({
65
- name: "Pro",
66
- description: "For professional teams.",
67
- is_default: false,
68
- is_public: true,
69
- type: "paid",
70
- prices: [
71
- {
72
- amount: 2900,
73
- currency: "USD",
74
- billing_interval: "monthly",
75
- trial_period_days: 14,
76
- },
77
- {
78
- amount: 29000,
79
- currency: "USD",
80
- billing_interval: "yearly",
81
- trial_period_days: 14,
82
- },
83
- ],
84
- features: {
85
- seats: { value_limit: 5, is_hard_limit: true },
86
- ai_tokens: { value_limit: 1000, reset_period: "monthly" },
87
- },
88
- }),
89
- },
109
+ plans,
90
110
  });
91
111
  ```
92
112
 
package/dist/cli.js CHANGED
@@ -75,7 +75,9 @@ var logoutCommand = new Command2("logout").description("Clear stored Revstack cr
75
75
  }
76
76
  clearApiKey();
77
77
  console.log(
78
- "\n" + chalk2.green(" \u2714 Successfully logged out.\n") + chalk2.dim(" Credentials removed from ~/.revstack/credentials.json\n")
78
+ "\n" + chalk2.green(" \u2714 Successfully logged out.\n") + chalk2.dim(
79
+ " Credentials removed from ~/.revstack/credentials.json\n"
80
+ )
79
81
  );
80
82
  });
81
83
 
@@ -85,23 +87,47 @@ import chalk3 from "chalk";
85
87
  import fs2 from "fs";
86
88
  import path2 from "path";
87
89
  import { spawnSync } from "child_process";
90
+ import { fileURLToPath } from "url";
88
91
  import ora from "ora";
89
- var STARTER_FEATURES = `import { defineFeature } from "@revstackhq/core";
92
+
93
+ // src/commands/templates/starter.ts
94
+ var starter = {
95
+ features: `import { defineFeature } from "@revstackhq/core";
90
96
 
91
97
  export const features = {
92
- seats: defineFeature({
93
- name: "Seats",
94
- type: "static",
95
- unit_type: "count",
96
- }),
97
- ai_tokens: defineFeature({
98
- name: "AI Tokens",
99
- type: "metered",
100
- unit_type: "count",
98
+ seats: defineFeature({ name: "Seats", type: "static", unit_type: "count" }),
99
+ priority_support: defineFeature({ name: "Priority Support", type: "boolean", unit_type: "custom" }),
100
+ };
101
+ `,
102
+ addons: `import { defineAddon } from "@revstackhq/core";
103
+ import { features } from "./features";
104
+
105
+ export const addons = {
106
+ extra_seats: defineAddon<typeof features>({
107
+ name: "5 Extra Seats",
108
+ description: "Add 5 more team members to your workspace.",
109
+ type: "recurring",
110
+ prices: [
111
+ { amount: 1500, currency: "USD", billing_interval: "monthly" }
112
+ ],
113
+ features: {
114
+ seats: { value_limit: 5, type: "increment", is_hard_limit: false },
115
+ }
101
116
  }),
117
+ vip_support: defineAddon<typeof features>({
118
+ name: "Priority Support",
119
+ description: "24/7 Slack channel support.",
120
+ type: "recurring",
121
+ prices: [
122
+ { amount: 9900, currency: "USD", billing_interval: "monthly" }
123
+ ],
124
+ features: {
125
+ priority_support: { has_access: true },
126
+ }
127
+ })
102
128
  };
103
- `;
104
- var STARTER_PLANS = `import { definePlan } from "@revstackhq/core";
129
+ `,
130
+ plans: `import { definePlan } from "@revstackhq/core";
105
131
  import { features } from "./features";
106
132
 
107
133
  export const plans = {
@@ -120,54 +146,260 @@ export const plans = {
120
146
  is_default: false,
121
147
  is_public: true,
122
148
  type: "paid",
149
+ available_addons: ["extra_seats", "vip_support"],
123
150
  prices: [
124
- {
125
- amount: 2900,
126
- currency: "USD",
127
- billing_interval: "monthly",
128
- trial_period_days: 14,
129
- },
130
- {
131
- amount: 29000,
132
- currency: "USD",
133
- billing_interval: "yearly",
134
- trial_period_days: 14,
135
- }
151
+ { amount: 2900, currency: "USD", billing_interval: "monthly", trial_period_days: 14 }
136
152
  ],
137
153
  features: {
138
154
  seats: { value_limit: 5, is_hard_limit: true },
139
- ai_tokens: { value_limit: 1000, reset_period: "monthly" },
140
155
  },
141
156
  }),
142
157
  };
143
- `;
144
- var STARTER_CONFIG = `import { defineConfig } from "@revstackhq/core";
145
- import { features } from "./revstack/features";
146
- import { plans } from "./revstack/plans";
158
+ `,
159
+ index: `import { defineConfig } from "@revstackhq/core";
160
+ import { features } from "./features";
161
+ import { addons } from "./addons";
162
+ import { plans } from "./plans";
147
163
 
148
164
  export default defineConfig({
149
165
  features,
166
+ addons,
150
167
  plans,
151
168
  });
152
- `;
153
- var initCommand = new Command3("init").description("Scaffold a new revstack.config.ts in the current directory").action(async () => {
169
+ `,
170
+ root: `import config from "./revstack";
171
+
172
+ export default config;
173
+ `
174
+ };
175
+
176
+ // src/commands/templates/b2b-saas.ts
177
+ var b2bSaas = {
178
+ features: `import { defineFeature } from "@revstackhq/core";
179
+
180
+ export const features = {
181
+ active_users: defineFeature({ name: "Active Users", type: "static", unit_type: "count" }),
182
+ api_access: defineFeature({ name: "API Access", type: "boolean", unit_type: "custom" }),
183
+ custom_domain: defineFeature({ name: "Custom Domain", type: "boolean", unit_type: "custom" }),
184
+ };
185
+ `,
186
+ addons: `import { defineAddon } from "@revstackhq/core";
187
+ import { features } from "./features";
188
+
189
+ export const addons = {
190
+ extra_users: defineAddon<typeof features>({
191
+ name: "10 Extra Users",
192
+ description: "Add 10 more active users to your workspace.",
193
+ type: "recurring",
194
+ prices: [
195
+ { amount: 5000, currency: "USD", billing_interval: "monthly" }
196
+ ],
197
+ features: {
198
+ active_users: { value_limit: 10, type: "increment", is_hard_limit: true },
199
+ }
200
+ }),
201
+ dedicated_support: defineAddon<typeof features>({
202
+ name: "Dedicated Support",
203
+ description: "Enterprise SLA with 1-hour response time.",
204
+ type: "recurring",
205
+ prices: [
206
+ { amount: 49900, currency: "USD", billing_interval: "monthly" }
207
+ ],
208
+ features: {}
209
+ })
210
+ };
211
+ `,
212
+ plans: `import { definePlan } from "@revstackhq/core";
213
+ import { features } from "./features";
214
+
215
+ export const plans = {
216
+ default: definePlan<typeof features>({
217
+ name: "Default",
218
+ description: "Automatically created default plan for guests.",
219
+ is_default: true,
220
+ is_public: false,
221
+ type: "free",
222
+ features: {},
223
+ }),
224
+ startup: definePlan<typeof features>({
225
+ name: "Startup",
226
+ description: "For small teams getting started.",
227
+ is_default: false,
228
+ is_public: true,
229
+ type: "paid",
230
+ available_addons: ["extra_users"],
231
+ prices: [
232
+ { amount: 9900, currency: "USD", billing_interval: "monthly" }
233
+ ],
234
+ features: {
235
+ active_users: { value_limit: 10, is_hard_limit: true },
236
+ api_access: { value_bool: false },
237
+ custom_domain: { value_bool: false },
238
+ },
239
+ }),
240
+ enterprise: definePlan<typeof features>({
241
+ name: "Enterprise",
242
+ description: "Advanced features for scale.",
243
+ is_default: false,
244
+ is_public: true,
245
+ type: "paid",
246
+ available_addons: ["extra_users", "dedicated_support"],
247
+ prices: [
248
+ { amount: 49900, currency: "USD", billing_interval: "monthly" }
249
+ ],
250
+ features: {
251
+ active_users: { value_limit: 100, is_hard_limit: false },
252
+ api_access: { value_bool: true },
253
+ custom_domain: { value_bool: true },
254
+ },
255
+ }),
256
+ };
257
+ `,
258
+ index: `import { defineConfig } from "@revstackhq/core";
259
+ import { features } from "./features";
260
+ import { addons } from "./addons";
261
+ import { plans } from "./plans";
262
+
263
+ export default defineConfig({
264
+ features,
265
+ addons,
266
+ plans,
267
+ });
268
+ `,
269
+ root: `import config from "./revstack";
270
+
271
+ export default config;
272
+ `
273
+ };
274
+
275
+ // src/commands/templates/usage-based.ts
276
+ var usageBased = {
277
+ features: `import { defineFeature } from "@revstackhq/core";
278
+
279
+ export const features = {
280
+ api_requests: defineFeature({ name: "API Requests", type: "metered", unit_type: "requests" }),
281
+ storage_gb: defineFeature({ name: "Storage (GB)", type: "metered", unit_type: "custom" }),
282
+ };
283
+ `,
284
+ addons: `import { defineAddon } from "@revstackhq/core";
285
+ import { features } from "./features";
286
+
287
+ export const addons = {
288
+ premium_support: defineAddon<typeof features>({
289
+ name: "Premium Support",
290
+ description: "24/7 dedicated support.",
291
+ type: "recurring",
292
+ prices: [
293
+ { amount: 20000, currency: "USD", billing_interval: "monthly" }
294
+ ],
295
+ features: {}
296
+ })
297
+ };
298
+ `,
299
+ plans: `import { definePlan } from "@revstackhq/core";
300
+ import { features } from "./features";
301
+
302
+ export const plans = {
303
+ default: definePlan<typeof features>({
304
+ name: "Default",
305
+ description: "Automatically created default plan for guests.",
306
+ is_default: true,
307
+ is_public: false,
308
+ type: "free",
309
+ features: {},
310
+ }),
311
+ pay_as_you_go: definePlan<typeof features>({
312
+ name: "Pay As You Go",
313
+ description: "Flexible usage-based pricing.",
314
+ is_default: false,
315
+ is_public: true,
316
+ type: "paid",
317
+ available_addons: ["premium_support"],
318
+ prices: [
319
+ { amount: 0, currency: "USD", billing_interval: "monthly" } // Base platform fee
320
+ ],
321
+ features: {
322
+ api_requests: { value_limit: 10000, is_hard_limit: false, reset_period: "monthly" }, // 10k free requests per month
323
+ storage_gb: { value_limit: 5, is_hard_limit: false, reset_period: "never" }, // 5GB free storage lifetime
324
+ },
325
+ }),
326
+ };
327
+ `,
328
+ index: `import { defineConfig } from "@revstackhq/core";
329
+ import { features } from "./features";
330
+ import { addons } from "./addons";
331
+ import { plans } from "./plans";
332
+
333
+ export default defineConfig({
334
+ features,
335
+ addons,
336
+ plans,
337
+ });
338
+ `,
339
+ root: `import config from "./revstack";
340
+
341
+ export default config;
342
+ `
343
+ };
344
+
345
+ // src/commands/templates/index.ts
346
+ var TEMPLATES = {
347
+ starter,
348
+ "b2b-saas": b2bSaas,
349
+ "usage-based": usageBased
350
+ };
351
+
352
+ // src/commands/init.ts
353
+ var initCommand = new Command3("init").description("Scaffold a new revstack.config.ts in the current directory").option(
354
+ "-t, --template <name>",
355
+ "Choose a starting template (starter, b2b-saas, usage-based)",
356
+ "starter"
357
+ ).action(async (options) => {
358
+ const templateName = options.template || "starter";
359
+ const template = TEMPLATES[templateName];
360
+ if (!template) {
361
+ console.log(
362
+ chalk3.red(
363
+ `
364
+ \u2716 Unknown template "${templateName}". Available templates: ${Object.keys(TEMPLATES).join(", ")}
365
+ `
366
+ )
367
+ );
368
+ process.exit(1);
369
+ }
154
370
  const cwd = process.cwd();
155
371
  const configPath = path2.resolve(cwd, "revstack.config.ts");
156
- const revstackDir = path2.resolve(cwd, "revstack");
157
- const featuresPath = path2.resolve(revstackDir, "features.ts");
158
- const plansPath = path2.resolve(revstackDir, "plans.ts");
159
372
  if (fs2.existsSync(configPath)) {
160
373
  console.log(
161
374
  "\n" + chalk3.yellow(" \u26A0 revstack.config.ts already exists.\n") + chalk3.dim(" Delete it first if you want to start fresh.\n")
162
375
  );
163
376
  process.exit(1);
164
377
  }
378
+ const revstackDir = path2.resolve(cwd, "revstack");
165
379
  if (!fs2.existsSync(revstackDir)) {
166
380
  fs2.mkdirSync(revstackDir, { recursive: true });
167
381
  }
168
- fs2.writeFileSync(featuresPath, STARTER_FEATURES, "utf-8");
169
- fs2.writeFileSync(plansPath, STARTER_PLANS, "utf-8");
170
- fs2.writeFileSync(configPath, STARTER_CONFIG, "utf-8");
382
+ fs2.writeFileSync(
383
+ path2.resolve(revstackDir, "features.ts"),
384
+ template.features,
385
+ "utf-8"
386
+ );
387
+ fs2.writeFileSync(
388
+ path2.resolve(revstackDir, "addons.ts"),
389
+ template.addons,
390
+ "utf-8"
391
+ );
392
+ fs2.writeFileSync(
393
+ path2.resolve(revstackDir, "plans.ts"),
394
+ template.plans,
395
+ "utf-8"
396
+ );
397
+ fs2.writeFileSync(
398
+ path2.resolve(revstackDir, "index.ts"),
399
+ template.index,
400
+ "utf-8"
401
+ );
402
+ fs2.writeFileSync(configPath, template.root, "utf-8");
171
403
  let packageManager = "npm";
172
404
  if (fs2.existsSync(path2.resolve(cwd, "pnpm-lock.yaml"))) {
173
405
  packageManager = "pnpm";
@@ -186,8 +418,15 @@ var initCommand = new Command3("init").description("Scaffold a new revstack.conf
186
418
  const spinner = ora("Installing @revstackhq/core...").start();
187
419
  let installFailed = false;
188
420
  try {
189
- const pkgVersion = process.env.npm_package_version || "dev";
190
- const tag = pkgVersion.includes("dev") ? "@dev" : "@latest";
421
+ const cliDir = path2.dirname(fileURLToPath(import.meta.url));
422
+ const pkgJsonPath = path2.resolve(cliDir, "../../package.json");
423
+ let cliVersion = "dev";
424
+ try {
425
+ const pkgData = fs2.readFileSync(pkgJsonPath, "utf-8");
426
+ cliVersion = JSON.parse(pkgData).version;
427
+ } catch (e) {
428
+ }
429
+ const tag = cliVersion.includes("dev") ? `@${cliVersion}` : "@latest";
191
430
  const pkgName = `@revstackhq/core${tag}`;
192
431
  const installArgs = packageManager === "yarn" ? ["add", pkgName] : packageManager === "pnpm" ? ["add", pkgName] : ["install", pkgName];
193
432
  let result = spawnSync(packageManager, installArgs, { cwd, shell: true });
@@ -217,9 +456,9 @@ var initCommand = new Command3("init").description("Scaffold a new revstack.conf
217
456
  );
218
457
  }
219
458
  console.log(
220
- "\n" + chalk3.green(" \u2714 Created revstack config structure\n") + "\n" + chalk3.dim(" Includes the ") + chalk3.white("Default Guest Plan") + chalk3.dim(" (required by Revstack).\n") + "\\n" + chalk3.dim(" Next steps:\\n") + (installFailed ? chalk3.dim(" 0. ") + chalk3.white(
221
- "Run " + chalk3.bold(packageManager + " install @revstackhq/core") + " manually\\n"
222
- ) : "") + chalk3.dim(" 1. ") + chalk3.white("Edit the config to match your billing model\\n") + chalk3.dim(" 2. ") + chalk3.white("Run ") + chalk3.bold("revstack login") + chalk3.white(" to authenticate\\n") + chalk3.dim(" 3. ") + chalk3.white("Run ") + chalk3.bold("revstack push") + chalk3.white(" to deploy\\n")
459
+ "\n" + chalk3.green(" \u2714 Created revstack config structure\n") + "\n" + chalk3.dim(" Includes the ") + chalk3.white("Default Guest Plan") + chalk3.dim(" (required by Revstack).\n") + "\n" + chalk3.dim(" Next steps:\n") + (installFailed ? chalk3.dim(" 0. ") + chalk3.white(
460
+ "Run " + chalk3.bold(packageManager + " install @revstackhq/core") + " manually\n"
461
+ ) : "") + chalk3.dim(" 1. ") + chalk3.white("Edit the config to match your billing model\n") + chalk3.dim(" 2. ") + chalk3.white("Run ") + chalk3.bold("revstack login") + chalk3.white(" to authenticate\n") + chalk3.dim(" 3. ") + chalk3.white("Run ") + chalk3.bold("revstack push") + chalk3.white(" to deploy\n")
223
462
  );
224
463
  });
225
464
 
@@ -258,6 +497,7 @@ async function loadLocalConfig(cwd) {
258
497
  }
259
498
 
260
499
  // src/commands/push.ts
500
+ import { validateConfig, RevstackValidationError } from "@revstackhq/core";
261
501
  var API_BASE = "https://app.revstack.dev";
262
502
  var DIFF_ICONS = {
263
503
  added: chalk5.green(" + "),
@@ -300,6 +540,37 @@ function requireAuth() {
300
540
  var pushCommand = new Command4("push").description("Push your local billing config to Revstack Cloud").option("-e, --env <environment>", "Target environment", "test").action(async (options) => {
301
541
  const apiKey = requireAuth();
302
542
  const config = await loadLocalConfig(process.cwd());
543
+ const validationSpinner = ora2({
544
+ text: "Validating billing configuration...",
545
+ prefixText: " "
546
+ }).start();
547
+ try {
548
+ validateConfig(config);
549
+ validationSpinner.succeed("Configuration validated");
550
+ } catch (error) {
551
+ if (error instanceof RevstackValidationError || error?.name === "RevstackValidationError") {
552
+ validationSpinner.fail("Configuration invalid");
553
+ console.error(
554
+ chalk5.red(
555
+ "\n \u2716 The billing configuration contains business logic errors:\n"
556
+ )
557
+ );
558
+ for (const err of error.errors || []) {
559
+ console.error(chalk5.red(` \u2022 ${err}`));
560
+ }
561
+ console.log();
562
+ process.exit(1);
563
+ }
564
+ validationSpinner.fail("Validation failed");
565
+ console.error(
566
+ chalk5.red(
567
+ `
568
+ An unexpected error occurred during validation: ${error?.message || String(error)}
569
+ `
570
+ )
571
+ );
572
+ process.exit(1);
573
+ }
303
574
  const spinner = ora2({
304
575
  text: "Calculating diff...",
305
576
  prefixText: " "