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

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.
@@ -1,4 +1,4 @@
1
- [?9001h[?1004h[?25l> @revstackhq/cli@0.0.0-dev-20260226061807 build C:\Users\flava\OneDrive\Desktop\Work\revstack-os\packages\cli
1
+ [?9001h[?1004h[?25l> @revstackhq/cli@0.0.0-dev-20260226063200 build C:\Users\flava\OneDrive\Desktop\Work\revstack-os\packages\cli
2
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
3
3
  CLI Using tsconfig: tsconfig.json
4
4
  CLI tsup v8.5.1
@@ -6,25 +6,25 @@ CLI Using tsup config: C:\Users\flava\OneDrive\Desktop\Work\revstack-os\packa
6
6
  CLI Target: node18
7
7
  CLI Cleaning output folder
8
8
  ESM Build start
9
- ESM dist\cli.js 18.25 KB
10
- ESM dist\utils\auth.js 971.00 B
11
- ESM dist\commands\init.js 5.02 KB
12
- ESM dist\commands\logout.js 1.15 KB
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
13
12
  ESM dist\commands\login.js 1.42 KB
14
- ESM dist\utils\config-loader.js 1.05 KB
13
+ ESM dist\utils\auth.js 971.00 B
15
14
  ESM dist\commands\pull.js 6.22 KB
15
+ ESM dist\utils\config-loader.js 1.05 KB
16
16
  ESM dist\commands\push.js 5.31 KB
17
- ESM dist\commands\init.js.map 9.64 KB
18
- ESM dist\utils\auth.js.map 2.36 KB
19
- ESM dist\cli.js.map 40.73 KB
17
+ ESM dist\cli.js.map 41.02 KB
20
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
21
22
  ESM dist\utils\config-loader.js.map 2.66 KB
22
- ESM dist\commands\login.js.map 3.89 KB
23
23
  ESM dist\commands\pull.js.map 14.72 KB
24
24
  ESM dist\commands\push.js.map 13.64 KB
25
- ESM ⚡️ Build success in 126ms
25
+ ESM ⚡️ Build success in 101ms
26
26
  DTS Build start
27
- DTS ⚡️ Build success in 4389ms
27
+ DTS ⚡️ Build success in 4049ms
28
28
  DTS dist\cli.d.ts 13.00 B
29
29
  DTS dist\commands\init.d.ts 353.00 B
30
30
  DTS dist\commands\login.d.ts 288.00 B
package/CHANGELOG.md CHANGED
@@ -1,5 +1,11 @@
1
1
  # @revstackhq/cli
2
2
 
3
+ ## 0.0.0-dev-20260226063200
4
+
5
+ ### Patch Changes
6
+
7
+ - fix: respect snapshot releases during core package scaffold
8
+
3
9
  ## 0.0.0-dev-20260226061807
4
10
 
5
11
  ### Minor Changes
package/dist/cli.js CHANGED
@@ -186,16 +186,19 @@ var initCommand = new Command3("init").description("Scaffold a new revstack.conf
186
186
  const spinner = ora("Installing @revstackhq/core...").start();
187
187
  let installFailed = false;
188
188
  try {
189
- const installArgs = packageManager === "yarn" ? ["add", "@revstackhq/core"] : packageManager === "pnpm" ? ["add", "@revstackhq/core"] : ["install", "@revstackhq/core"];
189
+ const pkgVersion = process.env.npm_package_version || "dev";
190
+ const tag = pkgVersion.includes("dev") ? "@dev" : "@latest";
191
+ const pkgName = `@revstackhq/core${tag}`;
192
+ const installArgs = packageManager === "yarn" ? ["add", pkgName] : packageManager === "pnpm" ? ["add", pkgName] : ["install", pkgName];
190
193
  let result = spawnSync(packageManager, installArgs, { cwd, shell: true });
191
194
  if (result.error || result.status !== 0) {
192
195
  if (packageManager === "pnpm") {
193
- result = spawnSync("pnpm", ["add", "-w", "@revstackhq/core"], {
196
+ result = spawnSync("pnpm", ["add", "-w", pkgName], {
194
197
  cwd,
195
198
  shell: true
196
199
  });
197
200
  } else if (packageManager === "yarn") {
198
- result = spawnSync("yarn", ["add", "-W", "@revstackhq/core"], {
201
+ result = spawnSync("yarn", ["add", "-W", pkgName], {
199
202
  cwd,
200
203
  shell: true
201
204
  });
package/dist/cli.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/cli.ts","../src/commands/login.ts","../src/utils/auth.ts","../src/commands/logout.ts","../src/commands/init.ts","../src/commands/push.ts","../src/utils/config-loader.ts","../src/commands/pull.ts"],"sourcesContent":["/**\r\n * @file cli.ts\r\n * @description Entry point for the Revstack CLI.\r\n * Registers all commands and parses process.argv.\r\n */\r\n\r\nimport { Command } from \"commander\";\r\nimport { createRequire } from \"node:module\";\r\n\r\nimport { loginCommand } from \"@/commands/login.js\";\r\nimport { logoutCommand } from \"@/commands/logout.js\";\r\nimport { initCommand } from \"@/commands/init.js\";\r\nimport { pushCommand } from \"@/commands/push.js\";\r\nimport { pullCommand } from \"@/commands/pull.js\";\r\n\r\nconst require = createRequire(import.meta.url);\r\nconst packageJson = require(\"../package.json\") as { version: string };\r\n\r\nconst program = new Command();\r\n\r\nprogram\r\n .name(\"revstack\")\r\n .description(\"The official CLI for Revstack — Billing as Code\")\r\n .version(packageJson.version);\r\n\r\nprogram.addCommand(loginCommand);\r\nprogram.addCommand(logoutCommand);\r\nprogram.addCommand(initCommand);\r\nprogram.addCommand(pushCommand);\r\nprogram.addCommand(pullCommand);\r\n\r\nprogram.parse(process.argv);\r\n","/**\r\n * @file commands/login.ts\r\n * @description Interactive authentication flow. Prompts the user for their\r\n * Revstack Secret Key and stores it globally for subsequent CLI commands.\r\n */\r\n\r\nimport { Command } from \"commander\";\r\nimport chalk from \"chalk\";\r\nimport prompts from \"prompts\";\r\nimport { setApiKey } from \"@/utils/auth.js\";\r\n\r\nexport const loginCommand = new Command(\"login\")\r\n .description(\"Authenticate with your Revstack Secret Key\")\r\n .action(async () => {\r\n console.log(\r\n \"\\n\" + chalk.bold(\" Revstack \") + chalk.dim(\"— Authentication\\n\")\r\n );\r\n\r\n const response = await prompts({\r\n type: \"password\",\r\n name: \"secretKey\",\r\n message: \"Enter your Revstack Secret Key\",\r\n validate: (value: string) =>\r\n value.startsWith(\"sk_\") ? true : \"Secret key must start with sk_\",\r\n });\r\n\r\n if (!response.secretKey) {\r\n console.log(chalk.dim(\"\\n Login cancelled.\\n\"));\r\n process.exit(0);\r\n }\r\n\r\n setApiKey(response.secretKey);\r\n\r\n console.log(\r\n \"\\n\" +\r\n chalk.green(\" ✔ Authenticated successfully!\\n\") +\r\n chalk.dim(\" Credentials saved to ~/.revstack/credentials.json\\n\")\r\n );\r\n });\r\n","/**\r\n * @file utils/auth.ts\r\n * @description Manages global Revstack credentials stored at ~/.revstack/credentials.json.\r\n * Provides simple get/set helpers for the API key used by all authenticated commands.\r\n */\r\n\r\nimport fs from \"node:fs\";\r\nimport path from \"node:path\";\r\nimport os from \"node:os\";\r\n\r\nconst CREDENTIALS_DIR = path.join(os.homedir(), \".revstack\");\r\nconst CREDENTIALS_FILE = path.join(CREDENTIALS_DIR, \"credentials.json\");\r\n\r\ninterface Credentials {\r\n apiKey: string;\r\n}\r\n\r\n/**\r\n * Persist an API key to the global credentials file.\r\n * Creates `~/.revstack/` if it doesn't exist.\r\n */\r\nexport function setApiKey(key: string): void {\r\n if (!fs.existsSync(CREDENTIALS_DIR)) {\r\n fs.mkdirSync(CREDENTIALS_DIR, { recursive: true });\r\n }\r\n\r\n const credentials: Credentials = { apiKey: key };\r\n fs.writeFileSync(\r\n CREDENTIALS_FILE,\r\n JSON.stringify(credentials, null, 2),\r\n \"utf-8\"\r\n );\r\n}\r\n\r\n/**\r\n * Read the stored API key, or return `null` if none is configured.\r\n */\r\nexport function getApiKey(): string | null {\r\n if (!fs.existsSync(CREDENTIALS_FILE)) {\r\n return null;\r\n }\r\n\r\n try {\r\n const raw = fs.readFileSync(CREDENTIALS_FILE, \"utf-8\");\r\n const credentials: Credentials = JSON.parse(raw);\r\n return credentials.apiKey ?? null;\r\n } catch {\r\n return null;\r\n }\r\n}\r\n\r\n/**\r\n * Remove stored credentials. Used by `revstack logout`.\r\n */\r\nexport function clearApiKey(): void {\r\n if (fs.existsSync(CREDENTIALS_FILE)) {\r\n fs.unlinkSync(CREDENTIALS_FILE);\r\n }\r\n}\r\n","/**\r\n * @file commands/logout.ts\r\n * @description Clears stored Revstack credentials.\r\n */\r\n\r\nimport { Command } from \"commander\";\r\nimport chalk from \"chalk\";\r\nimport { clearApiKey, getApiKey } from \"@/utils/auth.js\";\r\n\r\nexport const logoutCommand = new Command(\"logout\")\r\n .description(\"Clear stored Revstack credentials\")\r\n .action(() => {\r\n if (!getApiKey()) {\r\n console.log(chalk.dim(\"\\n Not currently logged in.\\n\"));\r\n return;\r\n }\r\n\r\n clearApiKey();\r\n\r\n console.log(\r\n \"\\n\" +\r\n chalk.green(\" ✔ Successfully logged out.\\n\") +\r\n chalk.dim(\" Credentials removed from ~/.revstack/credentials.json\\n\")\r\n );\r\n });\r\n","/**\r\n * @file commands/init.ts\r\n * @description Scaffolds a new `revstack.config.ts` in the current directory.\r\n * Generates a starter config with the immutable Default Guest Plan and\r\n * a sample Pro plan using type-safe helpers from @revstackhq/core.\r\n */\r\n\r\nimport { Command } from \"commander\";\r\nimport chalk from \"chalk\";\r\nimport fs from \"node:fs\";\r\nimport path from \"node:path\";\r\nimport { spawnSync } from \"node:child_process\";\r\nimport ora from \"ora\";\r\n\r\nconst STARTER_FEATURES = `import { defineFeature } from \"@revstackhq/core\";\r\n\r\nexport const features = {\r\n seats: defineFeature({\r\n name: \"Seats\",\r\n type: \"static\",\r\n unit_type: \"count\",\r\n }),\r\n ai_tokens: defineFeature({\r\n name: \"AI Tokens\",\r\n type: \"metered\",\r\n unit_type: \"count\",\r\n }),\r\n};\r\n`;\r\n\r\nconst STARTER_PLANS = `import { definePlan } from \"@revstackhq/core\";\r\nimport { features } from \"./features\";\r\n\r\nexport const plans = {\r\n // DO NOT DELETE: Automatically created default plan for guests.\r\n default: definePlan<typeof features>({\r\n name: \"Default\",\r\n description: \"Automatically created default plan for guests.\",\r\n is_default: true,\r\n is_public: false,\r\n type: \"free\",\r\n features: {},\r\n }),\r\n pro: definePlan<typeof features>({\r\n name: \"Pro\",\r\n description: \"For professional teams.\",\r\n is_default: false,\r\n is_public: true,\r\n type: \"paid\",\r\n prices: [\r\n {\r\n amount: 2900,\r\n currency: \"USD\",\r\n billing_interval: \"monthly\",\r\n trial_period_days: 14,\r\n },\r\n {\r\n amount: 29000,\r\n currency: \"USD\",\r\n billing_interval: \"yearly\",\r\n trial_period_days: 14,\r\n }\r\n ],\r\n features: {\r\n seats: { value_limit: 5, is_hard_limit: true },\r\n ai_tokens: { value_limit: 1000, reset_period: \"monthly\" },\r\n },\r\n }),\r\n};\r\n`;\r\n\r\nconst STARTER_CONFIG = `import { defineConfig } from \"@revstackhq/core\";\r\nimport { features } from \"./revstack/features\";\r\nimport { plans } from \"./revstack/plans\";\r\n\r\nexport default defineConfig({\r\n features,\r\n plans,\r\n});\r\n`;\r\n\r\nexport const initCommand = new Command(\"init\")\r\n .description(\"Scaffold a new revstack.config.ts in the current directory\")\r\n .action(async () => {\r\n const cwd = process.cwd();\r\n const configPath = path.resolve(cwd, \"revstack.config.ts\");\r\n const revstackDir = path.resolve(cwd, \"revstack\");\r\n const featuresPath = path.resolve(revstackDir, \"features.ts\");\r\n const plansPath = path.resolve(revstackDir, \"plans.ts\");\r\n\r\n if (fs.existsSync(configPath)) {\r\n console.log(\r\n \"\\n\" +\r\n chalk.yellow(\" ⚠ revstack.config.ts already exists.\\n\") +\r\n chalk.dim(\" Delete it first if you want to start fresh.\\n\"),\r\n );\r\n process.exit(1);\r\n }\r\n\r\n // Step 1: Create revstack directory and files\r\n if (!fs.existsSync(revstackDir)) {\r\n fs.mkdirSync(revstackDir, { recursive: true });\r\n }\r\n fs.writeFileSync(featuresPath, STARTER_FEATURES, \"utf-8\");\r\n fs.writeFileSync(plansPath, STARTER_PLANS, \"utf-8\");\r\n fs.writeFileSync(configPath, STARTER_CONFIG, \"utf-8\");\r\n\r\n // Step 2: Detect package manager & verify package.json\r\n let packageManager = \"npm\";\r\n if (fs.existsSync(path.resolve(cwd, \"pnpm-lock.yaml\"))) {\r\n packageManager = \"pnpm\";\r\n } else if (fs.existsSync(path.resolve(cwd, \"yarn.lock\"))) {\r\n packageManager = \"yarn\";\r\n } else if (fs.existsSync(path.resolve(cwd, \"package-lock.json\"))) {\r\n packageManager = \"npm\";\r\n }\r\n\r\n const packageJsonPath = path.resolve(cwd, \"package.json\");\r\n if (!fs.existsSync(packageJsonPath)) {\r\n // Create a default package.json if it doesn't exist\r\n try {\r\n spawnSync(\"npm\", [\"init\", \"-y\"], { cwd, stdio: \"ignore\", shell: true });\r\n } catch (err) {\r\n // Ignore initialization errors; the install command may still work or provide a better error.\r\n }\r\n }\r\n\r\n // Step 3: Install @revstackhq/core\r\n const spinner = ora(\"Installing @revstackhq/core...\").start();\r\n let installFailed = false;\r\n\r\n try {\r\n const installArgs =\r\n packageManager === \"yarn\"\r\n ? [\"add\", \"@revstackhq/core\"]\r\n : packageManager === \"pnpm\"\r\n ? [\"add\", \"@revstackhq/core\"]\r\n : [\"install\", \"@revstackhq/core\"];\r\n\r\n let result = spawnSync(packageManager, installArgs, { cwd, shell: true });\r\n if (result.error || result.status !== 0) {\r\n if (packageManager === \"pnpm\") {\r\n result = spawnSync(\"pnpm\", [\"add\", \"-w\", \"@revstackhq/core\"], {\r\n cwd,\r\n shell: true,\r\n });\r\n } else if (packageManager === \"yarn\") {\r\n result = spawnSync(\"yarn\", [\"add\", \"-W\", \"@revstackhq/core\"], {\r\n cwd,\r\n shell: true,\r\n });\r\n }\r\n }\r\n\r\n if (result.error || result.status !== 0) {\r\n throw new Error(\r\n \"Install failed: \" +\r\n (result.stderr\r\n ? result.stderr.toString()\r\n : result.stdout\r\n ? result.stdout.toString()\r\n : \"Unknown error\"),\r\n );\r\n }\r\n spinner.succeed(\"Dependencies installed\");\r\n } catch (err: any) {\r\n installFailed = true;\r\n spinner.fail(\r\n \"Failed to install @revstackhq/core automatically (\" +\r\n packageManager +\r\n \"). Reason: \" +\r\n err.message,\r\n );\r\n }\r\n\r\n // Step 4: Final Success Message\r\n console.log(\r\n \"\\n\" +\r\n chalk.green(\" ✔ Created revstack config structure\\n\") +\r\n \"\\n\" +\r\n chalk.dim(\" Includes the \") +\r\n chalk.white(\"Default Guest Plan\") +\r\n chalk.dim(\" (required by Revstack).\\n\") +\r\n \"\\\\n\" +\r\n chalk.dim(\" Next steps:\\\\n\") +\r\n (installFailed\r\n ? chalk.dim(\" 0. \") +\r\n chalk.white(\r\n \"Run \" +\r\n chalk.bold(packageManager + \" install @revstackhq/core\") +\r\n \" manually\\\\n\",\r\n )\r\n : \"\") +\r\n chalk.dim(\" 1. \") +\r\n chalk.white(\"Edit the config to match your billing model\\\\n\") +\r\n chalk.dim(\" 2. \") +\r\n chalk.white(\"Run \") +\r\n chalk.bold(\"revstack login\") +\r\n chalk.white(\" to authenticate\\\\n\") +\r\n chalk.dim(\" 3. \") +\r\n chalk.white(\"Run \") +\r\n chalk.bold(\"revstack push\") +\r\n chalk.white(\" to deploy\\\\n\"),\r\n );\r\n });\r\n","/**\r\n * @file commands/push.ts\r\n * @description The core deployment command. Loads the local config, sends it\r\n * to Revstack Cloud for diffing, presents the changes, and (upon confirmation)\r\n * pushes the config to production.\r\n */\r\n\r\nimport { Command } from \"commander\";\r\nimport chalk from \"chalk\";\r\nimport prompts from \"prompts\";\r\nimport ora from \"ora\";\r\nimport { getApiKey } from \"@/utils/auth\";\r\nimport { loadLocalConfig } from \"@/utils/config-loader\";\r\n\r\n// ─── Types ───────────────────────────────────────────────────\r\n\r\ninterface DiffEntry {\r\n action: \"added\" | \"removed\" | \"updated\";\r\n entity: string;\r\n id: string;\r\n message: string;\r\n}\r\n\r\ninterface DiffResponse {\r\n diff: DiffEntry[];\r\n canPush: boolean;\r\n blockedReason?: string;\r\n}\r\n\r\n// ─── Helpers ─────────────────────────────────────────────────\r\n\r\nconst API_BASE = \"https://app.revstack.dev\";\r\n\r\nconst DIFF_ICONS: Record<DiffEntry[\"action\"], string> = {\r\n added: chalk.green(\" + \"),\r\n removed: chalk.red(\" − \"),\r\n updated: chalk.yellow(\" ~ \"),\r\n};\r\n\r\nconst DIFF_COLORS: Record<DiffEntry[\"action\"], (text: string) => string> = {\r\n added: chalk.green,\r\n removed: chalk.red,\r\n updated: chalk.yellow,\r\n};\r\n\r\nfunction printDiff(diff: DiffEntry[]): void {\r\n if (diff.length === 0) {\r\n console.log(\r\n chalk.dim(\"\\n No changes detected. Your config is up to date.\\n\")\r\n );\r\n return;\r\n }\r\n\r\n console.log(chalk.bold(\"\\n Changes:\\n\"));\r\n\r\n for (const entry of diff) {\r\n const icon = DIFF_ICONS[entry.action];\r\n const color = DIFF_COLORS[entry.action];\r\n const label = chalk.dim(`[${entry.entity}]`);\r\n console.log(\r\n `${icon}${color(entry.id)} ${label} ${chalk.white(entry.message)}`\r\n );\r\n }\r\n\r\n console.log();\r\n}\r\n\r\nfunction requireAuth(): string {\r\n const apiKey = getApiKey();\r\n\r\n if (!apiKey) {\r\n console.error(\r\n \"\\n\" +\r\n chalk.red(\" ✖ Not authenticated.\\n\") +\r\n chalk.dim(\" Run \") +\r\n chalk.bold(\"revstack login\") +\r\n chalk.dim(\" first.\\n\")\r\n );\r\n process.exit(1);\r\n }\r\n\r\n return apiKey;\r\n}\r\n\r\n// ─── Command ─────────────────────────────────────────────────\r\n\r\nexport const pushCommand = new Command(\"push\")\r\n .description(\"Push your local billing config to Revstack Cloud\")\r\n .option(\"-e, --env <environment>\", \"Target environment\", \"test\")\r\n .action(async (options: { env: string }) => {\r\n const apiKey = requireAuth();\r\n const config = await loadLocalConfig(process.cwd());\r\n\r\n // ── Step 1: Compute diff ──────────────────────────────────\r\n\r\n const spinner = ora({\r\n text: \"Calculating diff...\",\r\n prefixText: \" \",\r\n }).start();\r\n\r\n let diffResponse: DiffResponse;\r\n\r\n try {\r\n const res = await fetch(`${API_BASE}/api/v1/cli/diff`, {\r\n method: \"POST\",\r\n headers: {\r\n \"Content-Type\": \"application/json\",\r\n Authorization: `Bearer ${apiKey}`,\r\n },\r\n body: JSON.stringify({ env: options.env, config }),\r\n });\r\n\r\n if (!res.ok) {\r\n spinner.fail(\"Failed to calculate diff\");\r\n console.error(\r\n chalk.red(`\\n API returned ${res.status}: ${res.statusText}\\n`)\r\n );\r\n process.exit(1);\r\n }\r\n\r\n diffResponse = (await res.json()) as DiffResponse;\r\n spinner.succeed(\"Diff calculated\");\r\n } catch (error: unknown) {\r\n spinner.fail(\"Failed to reach Revstack Cloud\");\r\n console.error(chalk.red(`\\n ${(error as Error).message}\\n`));\r\n process.exit(1);\r\n }\r\n\r\n // ── Step 2: Present diff ──────────────────────────────────\r\n\r\n printDiff(diffResponse.diff);\r\n\r\n if (diffResponse.diff.length === 0) {\r\n return;\r\n }\r\n\r\n // ── Step 3: Check if push is allowed ──────────────────────\r\n\r\n if (!diffResponse.canPush) {\r\n console.log(\r\n chalk.red(\" ✖ Push is blocked.\\n\") +\r\n chalk.dim(\r\n ` ${diffResponse.blockedReason ?? \"The server rejected this configuration.\"}\\n`\r\n )\r\n );\r\n process.exit(1);\r\n }\r\n\r\n // ── Step 4: Confirm ───────────────────────────────────────\r\n\r\n const envLabel =\r\n options.env === \"production\"\r\n ? chalk.red.bold(options.env)\r\n : chalk.cyan.bold(options.env);\r\n\r\n const { confirm } = await prompts({\r\n type: \"confirm\",\r\n name: \"confirm\",\r\n message: `Apply these changes to ${envLabel}?`,\r\n initial: false,\r\n });\r\n\r\n if (!confirm) {\r\n console.log(chalk.dim(\"\\n Push cancelled.\\n\"));\r\n return;\r\n }\r\n\r\n // ── Step 5: Push ──────────────────────────────────────────\r\n\r\n const pushSpinner = ora({\r\n text: `Pushing to ${options.env}...`,\r\n prefixText: \" \",\r\n }).start();\r\n\r\n try {\r\n const res = await fetch(`${API_BASE}/api/v1/cli/push`, {\r\n method: \"POST\",\r\n headers: {\r\n \"Content-Type\": \"application/json\",\r\n Authorization: `Bearer ${apiKey}`,\r\n },\r\n body: JSON.stringify({ env: options.env, config }),\r\n });\r\n\r\n if (!res.ok) {\r\n pushSpinner.fail(\"Push failed\");\r\n console.error(\r\n chalk.red(`\\n API returned ${res.status}: ${res.statusText}\\n`)\r\n );\r\n process.exit(1);\r\n }\r\n\r\n pushSpinner.succeed(\"Pushed successfully\");\r\n console.log(\r\n \"\\n\" +\r\n chalk.green(\" ✔ Config deployed to \") +\r\n envLabel +\r\n \"\\n\" +\r\n chalk.dim(\" Changes are now live.\\n\")\r\n );\r\n } catch (error: unknown) {\r\n pushSpinner.fail(\"Push failed\");\r\n console.error(chalk.red(`\\n ${(error as Error).message}\\n`));\r\n process.exit(1);\r\n }\r\n });\r\n","/**\r\n * @file utils/config-loader.ts\r\n * @description Loads and evaluates the user's `revstack.config.ts` at runtime\r\n * using jiti (just-in-time TypeScript compilation). Returns a sanitized,\r\n * JSON-safe representation of the config for network transmission.\r\n */\r\n\r\nimport { createJiti } from \"jiti\";\r\nimport path from \"node:path\";\r\nimport chalk from \"chalk\";\r\n\r\n/**\r\n * Load the `revstack.config.ts` from the given directory.\r\n *\r\n * @param cwd - The directory to search for `revstack.config.ts`.\r\n * @returns The parsed and sanitized configuration object.\r\n */\r\nexport async function loadLocalConfig(\r\n cwd: string\r\n): Promise<Record<string, unknown>> {\r\n const configPath = path.resolve(cwd, \"revstack.config.ts\");\r\n\r\n try {\r\n const jiti = createJiti(cwd);\r\n const module = (await jiti.import(configPath)) as Record<string, unknown>;\r\n const config = (module.default ?? module) as Record<string, unknown>;\r\n\r\n // Sanitize: strip functions, class instances, and non-serializable data.\r\n // This ensures we only send plain JSON to the Revstack Cloud API.\r\n return JSON.parse(JSON.stringify(config));\r\n } catch (error: unknown) {\r\n const err = error as NodeJS.ErrnoException;\r\n\r\n if (\r\n err.code === \"ERR_MODULE_NOT_FOUND\" ||\r\n err.code === \"ENOENT\" ||\r\n err.code === \"MODULE_NOT_FOUND\"\r\n ) {\r\n console.error(\r\n chalk.red(\r\n \"\\n ✖ Could not find revstack.config.ts in the current directory.\\n\"\r\n ) +\r\n chalk.dim(\" Run \") +\r\n chalk.bold(\"revstack init\") +\r\n chalk.dim(\" to create one.\\n\")\r\n );\r\n } else {\r\n console.error(\r\n chalk.red(\"\\n ✖ Failed to parse revstack.config.ts\\n\") +\r\n chalk.dim(\" \" + (err.message ?? String(error))) +\r\n \"\\n\"\r\n );\r\n }\r\n\r\n process.exit(1);\r\n }\r\n}\r\n","/**\r\n * @file commands/pull.ts\r\n * @description Fetches the current billing configuration from Revstack Cloud\r\n * and writes it back to the local `revstack.config.ts` file, overwriting\r\n * the existing config after user confirmation.\r\n */\r\n\r\nimport { Command } from \"commander\";\r\nimport chalk from \"chalk\";\r\nimport ora from \"ora\";\r\nimport prompts from \"prompts\";\r\nimport fs from \"node:fs\";\r\nimport path from \"node:path\";\r\nimport { getApiKey } from \"@/utils/auth\";\r\n\r\n// ─── Types ───────────────────────────────────────────────────\r\n\r\ninterface RemoteFeature {\r\n name: string;\r\n type: string;\r\n unit_type: string;\r\n description?: string;\r\n}\r\n\r\ninterface RemotePrice {\r\n amount: number;\r\n currency: string;\r\n billing_interval: string;\r\n trial_period_days?: number;\r\n}\r\n\r\ninterface RemotePlanFeature {\r\n value_limit?: number;\r\n value_bool?: boolean;\r\n value_text?: string;\r\n is_hard_limit?: boolean;\r\n reset_period?: string;\r\n}\r\n\r\ninterface RemotePlan {\r\n name: string;\r\n description?: string;\r\n is_default: boolean;\r\n is_public: boolean;\r\n type: string;\r\n prices?: RemotePrice[];\r\n features: Record<string, RemotePlanFeature>;\r\n}\r\n\r\ninterface RemoteConfig {\r\n features: Record<string, RemoteFeature>;\r\n plans: Record<string, RemotePlan>;\r\n}\r\n\r\nfunction serializeObject(\r\n obj: Record<string, unknown>,\r\n depth: number = 0,\r\n): string {\r\n const entries = Object.entries(obj);\r\n if (entries.length === 0) return \"{}\";\r\n\r\n const pad = \" \".repeat(depth + 1);\r\n const closePad = \" \".repeat(depth);\r\n\r\n const lines = entries\r\n .map(([key, value]) => {\r\n if (value === undefined) return null;\r\n\r\n const formattedValue =\r\n typeof value === \"string\"\r\n ? `\"${value}\"`\r\n : typeof value === \"number\" || typeof value === \"boolean\"\r\n ? String(value)\r\n : Array.isArray(value)\r\n ? serializeArray(value, depth + 1)\r\n : typeof value === \"object\" && value !== null\r\n ? serializeObject(value as Record<string, unknown>, depth + 1)\r\n : String(value);\r\n\r\n return `${pad}${key}: ${formattedValue},`;\r\n })\r\n .filter(Boolean);\r\n\r\n return `{\\n${lines.join(\"\\n\")}\\n${closePad}}`;\r\n}\r\n\r\nfunction serializeArray(arr: unknown[], depth: number): string {\r\n if (arr.length === 0) return \"[]\";\r\n\r\n const pad = \" \".repeat(depth + 1);\r\n const closePad = \" \".repeat(depth);\r\n\r\n const items = arr.map((item) => {\r\n if (typeof item === \"object\" && item !== null) {\r\n return `${pad}${serializeObject(item as Record<string, unknown>, depth + 1)},`;\r\n }\r\n return `${pad}${JSON.stringify(item)},`;\r\n });\r\n\r\n return `[\\n${items.join(\"\\n\")}\\n${closePad}]`;\r\n}\r\n\r\nfunction generateFeaturesSource(config: RemoteConfig): string {\r\n const featureEntries = Object.entries(config.features).map(([slug, f]) => {\r\n const props: Record<string, unknown> = {\r\n name: f.name,\r\n type: f.type,\r\n unit_type: f.unit_type,\r\n };\r\n if (f.description) props.description = f.description;\r\n\r\n return ` ${slug}: defineFeature(${serializeObject(props, 2)}),`;\r\n });\r\n\r\n return `import { defineFeature } from \"@revstackhq/core\";\r\n\r\nexport const features = {\r\n${featureEntries.join(\"\\n\")}\r\n};\r\n`;\r\n}\r\n\r\nfunction generatePlansSource(config: RemoteConfig): string {\r\n const planEntries = Object.entries(config.plans).map(([slug, plan]) => {\r\n const props: Record<string, unknown> = {\r\n name: plan.name,\r\n };\r\n if (plan.description) props.description = plan.description;\r\n props.is_default = plan.is_default;\r\n props.is_public = plan.is_public;\r\n props.type = plan.type;\r\n\r\n if (plan.prices && plan.prices.length > 0) {\r\n props.prices = plan.prices;\r\n }\r\n\r\n props.features = plan.features;\r\n\r\n const comment = plan.is_default\r\n ? ` // DO NOT DELETE: Automatically created default plan for guests.\\n`\r\n : \"\";\r\n\r\n return `${comment} ${slug}: definePlan<typeof features>(${serializeObject(props, 3)}),`;\r\n });\r\n\r\n return `import { definePlan } from \"@revstackhq/core\";\r\nimport { features } from \"./features\";\r\n\r\nexport const plans = {\r\n${planEntries.join(\"\\n\")}\r\n};\r\n`;\r\n}\r\n\r\nfunction generateRootConfigSource(): string {\r\n return `import { defineConfig } from \"@revstackhq/core\";\r\nimport { features } from \"./revstack/features\";\r\nimport { plans } from \"./revstack/plans\";\r\n\r\nexport default defineConfig({\r\n features,\r\n plans,\r\n});\r\n`;\r\n}\r\n\r\n// ─── Helpers ─────────────────────────────────────────────────\r\n\r\nconst API_BASE = \"https://app.revstack.dev\";\r\n\r\n// ─── Command ─────────────────────────────────────────────────\r\n\r\nexport const pullCommand = new Command(\"pull\")\r\n .description(\r\n \"Pull the remote billing config and overwrite local revstack.config.ts and revstack/ files\",\r\n )\r\n .option(\"-e, --env <environment>\", \"Target environment\", \"test\")\r\n .action(async (options: { env: string }) => {\r\n const apiKey = getApiKey();\r\n\r\n if (!apiKey) {\r\n console.error(\r\n \"\\n\" +\r\n chalk.red(\" ✖ Not authenticated.\\n\") +\r\n chalk.dim(\" Run \") +\r\n chalk.bold(\"revstack login\") +\r\n chalk.dim(\" first.\\n\"),\r\n );\r\n process.exit(1);\r\n }\r\n\r\n // ── 1. Fetch remote config ─────────────────────────────\r\n const spinner = ora({\r\n text: \"Fetching remote configuration...\",\r\n prefixText: \" \",\r\n }).start();\r\n\r\n let remoteConfig: RemoteConfig;\r\n\r\n try {\r\n const res = await fetch(\r\n `${API_BASE}/api/v1/cli/pull?env=${encodeURIComponent(options.env)}`,\r\n {\r\n headers: { Authorization: `Bearer ${apiKey}` },\r\n },\r\n );\r\n\r\n if (!res.ok) {\r\n spinner.fail(\"Failed to fetch remote config\");\r\n console.error(\r\n chalk.red(`\\n API returned ${res.status}: ${res.statusText}\\n`),\r\n );\r\n process.exit(1);\r\n }\r\n\r\n remoteConfig = (await res.json()) as RemoteConfig;\r\n spinner.succeed(\"Remote config fetched\");\r\n } catch (error: unknown) {\r\n spinner.fail(\"Failed to reach Revstack Cloud\");\r\n console.error(chalk.red(`\\n ${(error as Error).message}\\n`));\r\n process.exit(1);\r\n }\r\n\r\n // ── 2. Show summary ────────────────────────────────────\r\n const featureCount = Object.keys(remoteConfig.features).length;\r\n const planCount = Object.keys(remoteConfig.plans).length;\r\n\r\n console.log(\r\n \"\\n\" +\r\n chalk.dim(\" Remote state: \") +\r\n chalk.white(`${featureCount} features, ${planCount} plans`) +\r\n chalk.dim(` (${options.env})\\n`),\r\n );\r\n\r\n // ── 3. Confirm overwrite ───────────────────────────────\r\n const cwd = process.cwd();\r\n const configPath = path.resolve(cwd, \"revstack.config.ts\");\r\n const revstackDir = path.resolve(cwd, \"revstack\");\r\n const featuresPath = path.resolve(revstackDir, \"features.ts\");\r\n const plansPath = path.resolve(revstackDir, \"plans.ts\");\r\n\r\n const rootExists = fs.existsSync(configPath);\r\n const dirExists = fs.existsSync(revstackDir);\r\n\r\n if (rootExists || dirExists) {\r\n const { confirm } = await prompts({\r\n type: \"confirm\",\r\n name: \"confirm\",\r\n message:\r\n \"This will overwrite your local configuration files (revstack.config.ts and revstack/ data). Are you sure?\",\r\n initial: false,\r\n });\r\n\r\n if (!confirm) {\r\n console.log(chalk.dim(\"\\n Pull cancelled.\\n\"));\r\n return;\r\n }\r\n }\r\n\r\n // ── 4. Generate and write ──────────────────────────────\r\n if (!fs.existsSync(revstackDir)) {\r\n fs.mkdirSync(revstackDir, { recursive: true });\r\n }\r\n\r\n const featuresSource = generateFeaturesSource(remoteConfig);\r\n const plansSource = generatePlansSource(remoteConfig);\r\n const rootSource = generateRootConfigSource();\r\n\r\n fs.writeFileSync(featuresPath, featuresSource, \"utf-8\");\r\n fs.writeFileSync(plansPath, plansSource, \"utf-8\");\r\n fs.writeFileSync(configPath, rootSource, \"utf-8\");\r\n\r\n console.log(\r\n \"\\n\" +\r\n chalk.green(\" ✔ Local files updated from remote.\\n\") +\r\n chalk.dim(\" Review the files and run \") +\r\n chalk.bold(\"revstack push\") +\r\n chalk.dim(\" to re-deploy.\\n\"),\r\n );\r\n });\r\n"],"mappings":"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","names":["Command","Command","chalk","Command","chalk","Command","chalk","fs","path","Command","chalk","prompts","ora","path","chalk","chalk","Command","ora","prompts","Command","chalk","ora","prompts","fs","path","API_BASE","Command","chalk","ora","path","fs","prompts","require","Command"]}
1
+ {"version":3,"sources":["../src/cli.ts","../src/commands/login.ts","../src/utils/auth.ts","../src/commands/logout.ts","../src/commands/init.ts","../src/commands/push.ts","../src/utils/config-loader.ts","../src/commands/pull.ts"],"sourcesContent":["/**\r\n * @file cli.ts\r\n * @description Entry point for the Revstack CLI.\r\n * Registers all commands and parses process.argv.\r\n */\r\n\r\nimport { Command } from \"commander\";\r\nimport { createRequire } from \"node:module\";\r\n\r\nimport { loginCommand } from \"@/commands/login.js\";\r\nimport { logoutCommand } from \"@/commands/logout.js\";\r\nimport { initCommand } from \"@/commands/init.js\";\r\nimport { pushCommand } from \"@/commands/push.js\";\r\nimport { pullCommand } from \"@/commands/pull.js\";\r\n\r\nconst require = createRequire(import.meta.url);\r\nconst packageJson = require(\"../package.json\") as { version: string };\r\n\r\nconst program = new Command();\r\n\r\nprogram\r\n .name(\"revstack\")\r\n .description(\"The official CLI for Revstack — Billing as Code\")\r\n .version(packageJson.version);\r\n\r\nprogram.addCommand(loginCommand);\r\nprogram.addCommand(logoutCommand);\r\nprogram.addCommand(initCommand);\r\nprogram.addCommand(pushCommand);\r\nprogram.addCommand(pullCommand);\r\n\r\nprogram.parse(process.argv);\r\n","/**\r\n * @file commands/login.ts\r\n * @description Interactive authentication flow. Prompts the user for their\r\n * Revstack Secret Key and stores it globally for subsequent CLI commands.\r\n */\r\n\r\nimport { Command } from \"commander\";\r\nimport chalk from \"chalk\";\r\nimport prompts from \"prompts\";\r\nimport { setApiKey } from \"@/utils/auth.js\";\r\n\r\nexport const loginCommand = new Command(\"login\")\r\n .description(\"Authenticate with your Revstack Secret Key\")\r\n .action(async () => {\r\n console.log(\r\n \"\\n\" + chalk.bold(\" Revstack \") + chalk.dim(\"— Authentication\\n\")\r\n );\r\n\r\n const response = await prompts({\r\n type: \"password\",\r\n name: \"secretKey\",\r\n message: \"Enter your Revstack Secret Key\",\r\n validate: (value: string) =>\r\n value.startsWith(\"sk_\") ? true : \"Secret key must start with sk_\",\r\n });\r\n\r\n if (!response.secretKey) {\r\n console.log(chalk.dim(\"\\n Login cancelled.\\n\"));\r\n process.exit(0);\r\n }\r\n\r\n setApiKey(response.secretKey);\r\n\r\n console.log(\r\n \"\\n\" +\r\n chalk.green(\" ✔ Authenticated successfully!\\n\") +\r\n chalk.dim(\" Credentials saved to ~/.revstack/credentials.json\\n\")\r\n );\r\n });\r\n","/**\r\n * @file utils/auth.ts\r\n * @description Manages global Revstack credentials stored at ~/.revstack/credentials.json.\r\n * Provides simple get/set helpers for the API key used by all authenticated commands.\r\n */\r\n\r\nimport fs from \"node:fs\";\r\nimport path from \"node:path\";\r\nimport os from \"node:os\";\r\n\r\nconst CREDENTIALS_DIR = path.join(os.homedir(), \".revstack\");\r\nconst CREDENTIALS_FILE = path.join(CREDENTIALS_DIR, \"credentials.json\");\r\n\r\ninterface Credentials {\r\n apiKey: string;\r\n}\r\n\r\n/**\r\n * Persist an API key to the global credentials file.\r\n * Creates `~/.revstack/` if it doesn't exist.\r\n */\r\nexport function setApiKey(key: string): void {\r\n if (!fs.existsSync(CREDENTIALS_DIR)) {\r\n fs.mkdirSync(CREDENTIALS_DIR, { recursive: true });\r\n }\r\n\r\n const credentials: Credentials = { apiKey: key };\r\n fs.writeFileSync(\r\n CREDENTIALS_FILE,\r\n JSON.stringify(credentials, null, 2),\r\n \"utf-8\"\r\n );\r\n}\r\n\r\n/**\r\n * Read the stored API key, or return `null` if none is configured.\r\n */\r\nexport function getApiKey(): string | null {\r\n if (!fs.existsSync(CREDENTIALS_FILE)) {\r\n return null;\r\n }\r\n\r\n try {\r\n const raw = fs.readFileSync(CREDENTIALS_FILE, \"utf-8\");\r\n const credentials: Credentials = JSON.parse(raw);\r\n return credentials.apiKey ?? null;\r\n } catch {\r\n return null;\r\n }\r\n}\r\n\r\n/**\r\n * Remove stored credentials. Used by `revstack logout`.\r\n */\r\nexport function clearApiKey(): void {\r\n if (fs.existsSync(CREDENTIALS_FILE)) {\r\n fs.unlinkSync(CREDENTIALS_FILE);\r\n }\r\n}\r\n","/**\r\n * @file commands/logout.ts\r\n * @description Clears stored Revstack credentials.\r\n */\r\n\r\nimport { Command } from \"commander\";\r\nimport chalk from \"chalk\";\r\nimport { clearApiKey, getApiKey } from \"@/utils/auth.js\";\r\n\r\nexport const logoutCommand = new Command(\"logout\")\r\n .description(\"Clear stored Revstack credentials\")\r\n .action(() => {\r\n if (!getApiKey()) {\r\n console.log(chalk.dim(\"\\n Not currently logged in.\\n\"));\r\n return;\r\n }\r\n\r\n clearApiKey();\r\n\r\n console.log(\r\n \"\\n\" +\r\n chalk.green(\" ✔ Successfully logged out.\\n\") +\r\n chalk.dim(\" Credentials removed from ~/.revstack/credentials.json\\n\")\r\n );\r\n });\r\n","/**\r\n * @file commands/init.ts\r\n * @description Scaffolds a new `revstack.config.ts` in the current directory.\r\n * Generates a starter config with the immutable Default Guest Plan and\r\n * a sample Pro plan using type-safe helpers from @revstackhq/core.\r\n */\r\n\r\nimport { Command } from \"commander\";\r\nimport chalk from \"chalk\";\r\nimport fs from \"node:fs\";\r\nimport path from \"node:path\";\r\nimport { spawnSync } from \"node:child_process\";\r\nimport ora from \"ora\";\r\n\r\nconst STARTER_FEATURES = `import { defineFeature } from \"@revstackhq/core\";\r\n\r\nexport const features = {\r\n seats: defineFeature({\r\n name: \"Seats\",\r\n type: \"static\",\r\n unit_type: \"count\",\r\n }),\r\n ai_tokens: defineFeature({\r\n name: \"AI Tokens\",\r\n type: \"metered\",\r\n unit_type: \"count\",\r\n }),\r\n};\r\n`;\r\n\r\nconst STARTER_PLANS = `import { definePlan } from \"@revstackhq/core\";\r\nimport { features } from \"./features\";\r\n\r\nexport const plans = {\r\n // DO NOT DELETE: Automatically created default plan for guests.\r\n default: definePlan<typeof features>({\r\n name: \"Default\",\r\n description: \"Automatically created default plan for guests.\",\r\n is_default: true,\r\n is_public: false,\r\n type: \"free\",\r\n features: {},\r\n }),\r\n pro: definePlan<typeof features>({\r\n name: \"Pro\",\r\n description: \"For professional teams.\",\r\n is_default: false,\r\n is_public: true,\r\n type: \"paid\",\r\n prices: [\r\n {\r\n amount: 2900,\r\n currency: \"USD\",\r\n billing_interval: \"monthly\",\r\n trial_period_days: 14,\r\n },\r\n {\r\n amount: 29000,\r\n currency: \"USD\",\r\n billing_interval: \"yearly\",\r\n trial_period_days: 14,\r\n }\r\n ],\r\n features: {\r\n seats: { value_limit: 5, is_hard_limit: true },\r\n ai_tokens: { value_limit: 1000, reset_period: \"monthly\" },\r\n },\r\n }),\r\n};\r\n`;\r\n\r\nconst STARTER_CONFIG = `import { defineConfig } from \"@revstackhq/core\";\r\nimport { features } from \"./revstack/features\";\r\nimport { plans } from \"./revstack/plans\";\r\n\r\nexport default defineConfig({\r\n features,\r\n plans,\r\n});\r\n`;\r\n\r\nexport const initCommand = new Command(\"init\")\r\n .description(\"Scaffold a new revstack.config.ts in the current directory\")\r\n .action(async () => {\r\n const cwd = process.cwd();\r\n const configPath = path.resolve(cwd, \"revstack.config.ts\");\r\n const revstackDir = path.resolve(cwd, \"revstack\");\r\n const featuresPath = path.resolve(revstackDir, \"features.ts\");\r\n const plansPath = path.resolve(revstackDir, \"plans.ts\");\r\n\r\n if (fs.existsSync(configPath)) {\r\n console.log(\r\n \"\\n\" +\r\n chalk.yellow(\" ⚠ revstack.config.ts already exists.\\n\") +\r\n chalk.dim(\" Delete it first if you want to start fresh.\\n\"),\r\n );\r\n process.exit(1);\r\n }\r\n\r\n // Step 1: Create revstack directory and files\r\n if (!fs.existsSync(revstackDir)) {\r\n fs.mkdirSync(revstackDir, { recursive: true });\r\n }\r\n fs.writeFileSync(featuresPath, STARTER_FEATURES, \"utf-8\");\r\n fs.writeFileSync(plansPath, STARTER_PLANS, \"utf-8\");\r\n fs.writeFileSync(configPath, STARTER_CONFIG, \"utf-8\");\r\n\r\n // Step 2: Detect package manager & verify package.json\r\n let packageManager = \"npm\";\r\n if (fs.existsSync(path.resolve(cwd, \"pnpm-lock.yaml\"))) {\r\n packageManager = \"pnpm\";\r\n } else if (fs.existsSync(path.resolve(cwd, \"yarn.lock\"))) {\r\n packageManager = \"yarn\";\r\n } else if (fs.existsSync(path.resolve(cwd, \"package-lock.json\"))) {\r\n packageManager = \"npm\";\r\n }\r\n\r\n const packageJsonPath = path.resolve(cwd, \"package.json\");\r\n if (!fs.existsSync(packageJsonPath)) {\r\n // Create a default package.json if it doesn't exist\r\n try {\r\n spawnSync(\"npm\", [\"init\", \"-y\"], { cwd, stdio: \"ignore\", shell: true });\r\n } catch (err) {\r\n // Ignore initialization errors; the install command may still work or provide a better error.\r\n }\r\n }\r\n\r\n // Step 3: Install @revstackhq/core\r\n const spinner = ora(\"Installing @revstackhq/core...\").start();\r\n let installFailed = false;\r\n\r\n try {\r\n // Use @dev tag if the CLI itself is a dev snapshot\r\n const pkgVersion = process.env.npm_package_version || \"dev\";\r\n const tag = pkgVersion.includes(\"dev\") ? \"@dev\" : \"@latest\";\r\n const pkgName = `@revstackhq/core${tag}`;\r\n\r\n const installArgs =\r\n packageManager === \"yarn\"\r\n ? [\"add\", pkgName]\r\n : packageManager === \"pnpm\"\r\n ? [\"add\", pkgName]\r\n : [\"install\", pkgName];\r\n\r\n let result = spawnSync(packageManager, installArgs, { cwd, shell: true });\r\n if (result.error || result.status !== 0) {\r\n if (packageManager === \"pnpm\") {\r\n result = spawnSync(\"pnpm\", [\"add\", \"-w\", pkgName], {\r\n cwd,\r\n shell: true,\r\n });\r\n } else if (packageManager === \"yarn\") {\r\n result = spawnSync(\"yarn\", [\"add\", \"-W\", pkgName], {\r\n cwd,\r\n shell: true,\r\n });\r\n }\r\n }\r\n\r\n if (result.error || result.status !== 0) {\r\n throw new Error(\r\n \"Install failed: \" +\r\n (result.stderr\r\n ? result.stderr.toString()\r\n : result.stdout\r\n ? result.stdout.toString()\r\n : \"Unknown error\"),\r\n );\r\n }\r\n spinner.succeed(\"Dependencies installed\");\r\n } catch (err: any) {\r\n installFailed = true;\r\n spinner.fail(\r\n \"Failed to install @revstackhq/core automatically (\" +\r\n packageManager +\r\n \"). Reason: \" +\r\n err.message,\r\n );\r\n }\r\n\r\n // Step 4: Final Success Message\r\n console.log(\r\n \"\\n\" +\r\n chalk.green(\" ✔ Created revstack config structure\\n\") +\r\n \"\\n\" +\r\n chalk.dim(\" Includes the \") +\r\n chalk.white(\"Default Guest Plan\") +\r\n chalk.dim(\" (required by Revstack).\\n\") +\r\n \"\\\\n\" +\r\n chalk.dim(\" Next steps:\\\\n\") +\r\n (installFailed\r\n ? chalk.dim(\" 0. \") +\r\n chalk.white(\r\n \"Run \" +\r\n chalk.bold(packageManager + \" install @revstackhq/core\") +\r\n \" manually\\\\n\",\r\n )\r\n : \"\") +\r\n chalk.dim(\" 1. \") +\r\n chalk.white(\"Edit the config to match your billing model\\\\n\") +\r\n chalk.dim(\" 2. \") +\r\n chalk.white(\"Run \") +\r\n chalk.bold(\"revstack login\") +\r\n chalk.white(\" to authenticate\\\\n\") +\r\n chalk.dim(\" 3. \") +\r\n chalk.white(\"Run \") +\r\n chalk.bold(\"revstack push\") +\r\n chalk.white(\" to deploy\\\\n\"),\r\n );\r\n });\r\n","/**\r\n * @file commands/push.ts\r\n * @description The core deployment command. Loads the local config, sends it\r\n * to Revstack Cloud for diffing, presents the changes, and (upon confirmation)\r\n * pushes the config to production.\r\n */\r\n\r\nimport { Command } from \"commander\";\r\nimport chalk from \"chalk\";\r\nimport prompts from \"prompts\";\r\nimport ora from \"ora\";\r\nimport { getApiKey } from \"@/utils/auth\";\r\nimport { loadLocalConfig } from \"@/utils/config-loader\";\r\n\r\n// ─── Types ───────────────────────────────────────────────────\r\n\r\ninterface DiffEntry {\r\n action: \"added\" | \"removed\" | \"updated\";\r\n entity: string;\r\n id: string;\r\n message: string;\r\n}\r\n\r\ninterface DiffResponse {\r\n diff: DiffEntry[];\r\n canPush: boolean;\r\n blockedReason?: string;\r\n}\r\n\r\n// ─── Helpers ─────────────────────────────────────────────────\r\n\r\nconst API_BASE = \"https://app.revstack.dev\";\r\n\r\nconst DIFF_ICONS: Record<DiffEntry[\"action\"], string> = {\r\n added: chalk.green(\" + \"),\r\n removed: chalk.red(\" − \"),\r\n updated: chalk.yellow(\" ~ \"),\r\n};\r\n\r\nconst DIFF_COLORS: Record<DiffEntry[\"action\"], (text: string) => string> = {\r\n added: chalk.green,\r\n removed: chalk.red,\r\n updated: chalk.yellow,\r\n};\r\n\r\nfunction printDiff(diff: DiffEntry[]): void {\r\n if (diff.length === 0) {\r\n console.log(\r\n chalk.dim(\"\\n No changes detected. Your config is up to date.\\n\")\r\n );\r\n return;\r\n }\r\n\r\n console.log(chalk.bold(\"\\n Changes:\\n\"));\r\n\r\n for (const entry of diff) {\r\n const icon = DIFF_ICONS[entry.action];\r\n const color = DIFF_COLORS[entry.action];\r\n const label = chalk.dim(`[${entry.entity}]`);\r\n console.log(\r\n `${icon}${color(entry.id)} ${label} ${chalk.white(entry.message)}`\r\n );\r\n }\r\n\r\n console.log();\r\n}\r\n\r\nfunction requireAuth(): string {\r\n const apiKey = getApiKey();\r\n\r\n if (!apiKey) {\r\n console.error(\r\n \"\\n\" +\r\n chalk.red(\" ✖ Not authenticated.\\n\") +\r\n chalk.dim(\" Run \") +\r\n chalk.bold(\"revstack login\") +\r\n chalk.dim(\" first.\\n\")\r\n );\r\n process.exit(1);\r\n }\r\n\r\n return apiKey;\r\n}\r\n\r\n// ─── Command ─────────────────────────────────────────────────\r\n\r\nexport const pushCommand = new Command(\"push\")\r\n .description(\"Push your local billing config to Revstack Cloud\")\r\n .option(\"-e, --env <environment>\", \"Target environment\", \"test\")\r\n .action(async (options: { env: string }) => {\r\n const apiKey = requireAuth();\r\n const config = await loadLocalConfig(process.cwd());\r\n\r\n // ── Step 1: Compute diff ──────────────────────────────────\r\n\r\n const spinner = ora({\r\n text: \"Calculating diff...\",\r\n prefixText: \" \",\r\n }).start();\r\n\r\n let diffResponse: DiffResponse;\r\n\r\n try {\r\n const res = await fetch(`${API_BASE}/api/v1/cli/diff`, {\r\n method: \"POST\",\r\n headers: {\r\n \"Content-Type\": \"application/json\",\r\n Authorization: `Bearer ${apiKey}`,\r\n },\r\n body: JSON.stringify({ env: options.env, config }),\r\n });\r\n\r\n if (!res.ok) {\r\n spinner.fail(\"Failed to calculate diff\");\r\n console.error(\r\n chalk.red(`\\n API returned ${res.status}: ${res.statusText}\\n`)\r\n );\r\n process.exit(1);\r\n }\r\n\r\n diffResponse = (await res.json()) as DiffResponse;\r\n spinner.succeed(\"Diff calculated\");\r\n } catch (error: unknown) {\r\n spinner.fail(\"Failed to reach Revstack Cloud\");\r\n console.error(chalk.red(`\\n ${(error as Error).message}\\n`));\r\n process.exit(1);\r\n }\r\n\r\n // ── Step 2: Present diff ──────────────────────────────────\r\n\r\n printDiff(diffResponse.diff);\r\n\r\n if (diffResponse.diff.length === 0) {\r\n return;\r\n }\r\n\r\n // ── Step 3: Check if push is allowed ──────────────────────\r\n\r\n if (!diffResponse.canPush) {\r\n console.log(\r\n chalk.red(\" ✖ Push is blocked.\\n\") +\r\n chalk.dim(\r\n ` ${diffResponse.blockedReason ?? \"The server rejected this configuration.\"}\\n`\r\n )\r\n );\r\n process.exit(1);\r\n }\r\n\r\n // ── Step 4: Confirm ───────────────────────────────────────\r\n\r\n const envLabel =\r\n options.env === \"production\"\r\n ? chalk.red.bold(options.env)\r\n : chalk.cyan.bold(options.env);\r\n\r\n const { confirm } = await prompts({\r\n type: \"confirm\",\r\n name: \"confirm\",\r\n message: `Apply these changes to ${envLabel}?`,\r\n initial: false,\r\n });\r\n\r\n if (!confirm) {\r\n console.log(chalk.dim(\"\\n Push cancelled.\\n\"));\r\n return;\r\n }\r\n\r\n // ── Step 5: Push ──────────────────────────────────────────\r\n\r\n const pushSpinner = ora({\r\n text: `Pushing to ${options.env}...`,\r\n prefixText: \" \",\r\n }).start();\r\n\r\n try {\r\n const res = await fetch(`${API_BASE}/api/v1/cli/push`, {\r\n method: \"POST\",\r\n headers: {\r\n \"Content-Type\": \"application/json\",\r\n Authorization: `Bearer ${apiKey}`,\r\n },\r\n body: JSON.stringify({ env: options.env, config }),\r\n });\r\n\r\n if (!res.ok) {\r\n pushSpinner.fail(\"Push failed\");\r\n console.error(\r\n chalk.red(`\\n API returned ${res.status}: ${res.statusText}\\n`)\r\n );\r\n process.exit(1);\r\n }\r\n\r\n pushSpinner.succeed(\"Pushed successfully\");\r\n console.log(\r\n \"\\n\" +\r\n chalk.green(\" ✔ Config deployed to \") +\r\n envLabel +\r\n \"\\n\" +\r\n chalk.dim(\" Changes are now live.\\n\")\r\n );\r\n } catch (error: unknown) {\r\n pushSpinner.fail(\"Push failed\");\r\n console.error(chalk.red(`\\n ${(error as Error).message}\\n`));\r\n process.exit(1);\r\n }\r\n });\r\n","/**\r\n * @file utils/config-loader.ts\r\n * @description Loads and evaluates the user's `revstack.config.ts` at runtime\r\n * using jiti (just-in-time TypeScript compilation). Returns a sanitized,\r\n * JSON-safe representation of the config for network transmission.\r\n */\r\n\r\nimport { createJiti } from \"jiti\";\r\nimport path from \"node:path\";\r\nimport chalk from \"chalk\";\r\n\r\n/**\r\n * Load the `revstack.config.ts` from the given directory.\r\n *\r\n * @param cwd - The directory to search for `revstack.config.ts`.\r\n * @returns The parsed and sanitized configuration object.\r\n */\r\nexport async function loadLocalConfig(\r\n cwd: string\r\n): Promise<Record<string, unknown>> {\r\n const configPath = path.resolve(cwd, \"revstack.config.ts\");\r\n\r\n try {\r\n const jiti = createJiti(cwd);\r\n const module = (await jiti.import(configPath)) as Record<string, unknown>;\r\n const config = (module.default ?? module) as Record<string, unknown>;\r\n\r\n // Sanitize: strip functions, class instances, and non-serializable data.\r\n // This ensures we only send plain JSON to the Revstack Cloud API.\r\n return JSON.parse(JSON.stringify(config));\r\n } catch (error: unknown) {\r\n const err = error as NodeJS.ErrnoException;\r\n\r\n if (\r\n err.code === \"ERR_MODULE_NOT_FOUND\" ||\r\n err.code === \"ENOENT\" ||\r\n err.code === \"MODULE_NOT_FOUND\"\r\n ) {\r\n console.error(\r\n chalk.red(\r\n \"\\n ✖ Could not find revstack.config.ts in the current directory.\\n\"\r\n ) +\r\n chalk.dim(\" Run \") +\r\n chalk.bold(\"revstack init\") +\r\n chalk.dim(\" to create one.\\n\")\r\n );\r\n } else {\r\n console.error(\r\n chalk.red(\"\\n ✖ Failed to parse revstack.config.ts\\n\") +\r\n chalk.dim(\" \" + (err.message ?? String(error))) +\r\n \"\\n\"\r\n );\r\n }\r\n\r\n process.exit(1);\r\n }\r\n}\r\n","/**\r\n * @file commands/pull.ts\r\n * @description Fetches the current billing configuration from Revstack Cloud\r\n * and writes it back to the local `revstack.config.ts` file, overwriting\r\n * the existing config after user confirmation.\r\n */\r\n\r\nimport { Command } from \"commander\";\r\nimport chalk from \"chalk\";\r\nimport ora from \"ora\";\r\nimport prompts from \"prompts\";\r\nimport fs from \"node:fs\";\r\nimport path from \"node:path\";\r\nimport { getApiKey } from \"@/utils/auth\";\r\n\r\n// ─── Types ───────────────────────────────────────────────────\r\n\r\ninterface RemoteFeature {\r\n name: string;\r\n type: string;\r\n unit_type: string;\r\n description?: string;\r\n}\r\n\r\ninterface RemotePrice {\r\n amount: number;\r\n currency: string;\r\n billing_interval: string;\r\n trial_period_days?: number;\r\n}\r\n\r\ninterface RemotePlanFeature {\r\n value_limit?: number;\r\n value_bool?: boolean;\r\n value_text?: string;\r\n is_hard_limit?: boolean;\r\n reset_period?: string;\r\n}\r\n\r\ninterface RemotePlan {\r\n name: string;\r\n description?: string;\r\n is_default: boolean;\r\n is_public: boolean;\r\n type: string;\r\n prices?: RemotePrice[];\r\n features: Record<string, RemotePlanFeature>;\r\n}\r\n\r\ninterface RemoteConfig {\r\n features: Record<string, RemoteFeature>;\r\n plans: Record<string, RemotePlan>;\r\n}\r\n\r\nfunction serializeObject(\r\n obj: Record<string, unknown>,\r\n depth: number = 0,\r\n): string {\r\n const entries = Object.entries(obj);\r\n if (entries.length === 0) return \"{}\";\r\n\r\n const pad = \" \".repeat(depth + 1);\r\n const closePad = \" \".repeat(depth);\r\n\r\n const lines = entries\r\n .map(([key, value]) => {\r\n if (value === undefined) return null;\r\n\r\n const formattedValue =\r\n typeof value === \"string\"\r\n ? `\"${value}\"`\r\n : typeof value === \"number\" || typeof value === \"boolean\"\r\n ? String(value)\r\n : Array.isArray(value)\r\n ? serializeArray(value, depth + 1)\r\n : typeof value === \"object\" && value !== null\r\n ? serializeObject(value as Record<string, unknown>, depth + 1)\r\n : String(value);\r\n\r\n return `${pad}${key}: ${formattedValue},`;\r\n })\r\n .filter(Boolean);\r\n\r\n return `{\\n${lines.join(\"\\n\")}\\n${closePad}}`;\r\n}\r\n\r\nfunction serializeArray(arr: unknown[], depth: number): string {\r\n if (arr.length === 0) return \"[]\";\r\n\r\n const pad = \" \".repeat(depth + 1);\r\n const closePad = \" \".repeat(depth);\r\n\r\n const items = arr.map((item) => {\r\n if (typeof item === \"object\" && item !== null) {\r\n return `${pad}${serializeObject(item as Record<string, unknown>, depth + 1)},`;\r\n }\r\n return `${pad}${JSON.stringify(item)},`;\r\n });\r\n\r\n return `[\\n${items.join(\"\\n\")}\\n${closePad}]`;\r\n}\r\n\r\nfunction generateFeaturesSource(config: RemoteConfig): string {\r\n const featureEntries = Object.entries(config.features).map(([slug, f]) => {\r\n const props: Record<string, unknown> = {\r\n name: f.name,\r\n type: f.type,\r\n unit_type: f.unit_type,\r\n };\r\n if (f.description) props.description = f.description;\r\n\r\n return ` ${slug}: defineFeature(${serializeObject(props, 2)}),`;\r\n });\r\n\r\n return `import { defineFeature } from \"@revstackhq/core\";\r\n\r\nexport const features = {\r\n${featureEntries.join(\"\\n\")}\r\n};\r\n`;\r\n}\r\n\r\nfunction generatePlansSource(config: RemoteConfig): string {\r\n const planEntries = Object.entries(config.plans).map(([slug, plan]) => {\r\n const props: Record<string, unknown> = {\r\n name: plan.name,\r\n };\r\n if (plan.description) props.description = plan.description;\r\n props.is_default = plan.is_default;\r\n props.is_public = plan.is_public;\r\n props.type = plan.type;\r\n\r\n if (plan.prices && plan.prices.length > 0) {\r\n props.prices = plan.prices;\r\n }\r\n\r\n props.features = plan.features;\r\n\r\n const comment = plan.is_default\r\n ? ` // DO NOT DELETE: Automatically created default plan for guests.\\n`\r\n : \"\";\r\n\r\n return `${comment} ${slug}: definePlan<typeof features>(${serializeObject(props, 3)}),`;\r\n });\r\n\r\n return `import { definePlan } from \"@revstackhq/core\";\r\nimport { features } from \"./features\";\r\n\r\nexport const plans = {\r\n${planEntries.join(\"\\n\")}\r\n};\r\n`;\r\n}\r\n\r\nfunction generateRootConfigSource(): string {\r\n return `import { defineConfig } from \"@revstackhq/core\";\r\nimport { features } from \"./revstack/features\";\r\nimport { plans } from \"./revstack/plans\";\r\n\r\nexport default defineConfig({\r\n features,\r\n plans,\r\n});\r\n`;\r\n}\r\n\r\n// ─── Helpers ─────────────────────────────────────────────────\r\n\r\nconst API_BASE = \"https://app.revstack.dev\";\r\n\r\n// ─── Command ─────────────────────────────────────────────────\r\n\r\nexport const pullCommand = new Command(\"pull\")\r\n .description(\r\n \"Pull the remote billing config and overwrite local revstack.config.ts and revstack/ files\",\r\n )\r\n .option(\"-e, --env <environment>\", \"Target environment\", \"test\")\r\n .action(async (options: { env: string }) => {\r\n const apiKey = getApiKey();\r\n\r\n if (!apiKey) {\r\n console.error(\r\n \"\\n\" +\r\n chalk.red(\" ✖ Not authenticated.\\n\") +\r\n chalk.dim(\" Run \") +\r\n chalk.bold(\"revstack login\") +\r\n chalk.dim(\" first.\\n\"),\r\n );\r\n process.exit(1);\r\n }\r\n\r\n // ── 1. Fetch remote config ─────────────────────────────\r\n const spinner = ora({\r\n text: \"Fetching remote configuration...\",\r\n prefixText: \" \",\r\n }).start();\r\n\r\n let remoteConfig: RemoteConfig;\r\n\r\n try {\r\n const res = await fetch(\r\n `${API_BASE}/api/v1/cli/pull?env=${encodeURIComponent(options.env)}`,\r\n {\r\n headers: { Authorization: `Bearer ${apiKey}` },\r\n },\r\n );\r\n\r\n if (!res.ok) {\r\n spinner.fail(\"Failed to fetch remote config\");\r\n console.error(\r\n chalk.red(`\\n API returned ${res.status}: ${res.statusText}\\n`),\r\n );\r\n process.exit(1);\r\n }\r\n\r\n remoteConfig = (await res.json()) as RemoteConfig;\r\n spinner.succeed(\"Remote config fetched\");\r\n } catch (error: unknown) {\r\n spinner.fail(\"Failed to reach Revstack Cloud\");\r\n console.error(chalk.red(`\\n ${(error as Error).message}\\n`));\r\n process.exit(1);\r\n }\r\n\r\n // ── 2. Show summary ────────────────────────────────────\r\n const featureCount = Object.keys(remoteConfig.features).length;\r\n const planCount = Object.keys(remoteConfig.plans).length;\r\n\r\n console.log(\r\n \"\\n\" +\r\n chalk.dim(\" Remote state: \") +\r\n chalk.white(`${featureCount} features, ${planCount} plans`) +\r\n chalk.dim(` (${options.env})\\n`),\r\n );\r\n\r\n // ── 3. Confirm overwrite ───────────────────────────────\r\n const cwd = process.cwd();\r\n const configPath = path.resolve(cwd, \"revstack.config.ts\");\r\n const revstackDir = path.resolve(cwd, \"revstack\");\r\n const featuresPath = path.resolve(revstackDir, \"features.ts\");\r\n const plansPath = path.resolve(revstackDir, \"plans.ts\");\r\n\r\n const rootExists = fs.existsSync(configPath);\r\n const dirExists = fs.existsSync(revstackDir);\r\n\r\n if (rootExists || dirExists) {\r\n const { confirm } = await prompts({\r\n type: \"confirm\",\r\n name: \"confirm\",\r\n message:\r\n \"This will overwrite your local configuration files (revstack.config.ts and revstack/ data). Are you sure?\",\r\n initial: false,\r\n });\r\n\r\n if (!confirm) {\r\n console.log(chalk.dim(\"\\n Pull cancelled.\\n\"));\r\n return;\r\n }\r\n }\r\n\r\n // ── 4. Generate and write ──────────────────────────────\r\n if (!fs.existsSync(revstackDir)) {\r\n fs.mkdirSync(revstackDir, { recursive: true });\r\n }\r\n\r\n const featuresSource = generateFeaturesSource(remoteConfig);\r\n const plansSource = generatePlansSource(remoteConfig);\r\n const rootSource = generateRootConfigSource();\r\n\r\n fs.writeFileSync(featuresPath, featuresSource, \"utf-8\");\r\n fs.writeFileSync(plansPath, plansSource, \"utf-8\");\r\n fs.writeFileSync(configPath, rootSource, \"utf-8\");\r\n\r\n console.log(\r\n \"\\n\" +\r\n chalk.green(\" ✔ Local files updated from remote.\\n\") +\r\n chalk.dim(\" Review the files and run \") +\r\n chalk.bold(\"revstack push\") +\r\n chalk.dim(\" to re-deploy.\\n\"),\r\n );\r\n });\r\n"],"mappings":"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","names":["Command","Command","chalk","Command","chalk","Command","chalk","fs","path","Command","chalk","prompts","ora","path","chalk","chalk","Command","ora","prompts","Command","chalk","ora","prompts","fs","path","API_BASE","Command","chalk","ora","path","fs","prompts","require","Command"]}
@@ -107,16 +107,19 @@ var initCommand = new Command("init").description("Scaffold a new revstack.confi
107
107
  const spinner = ora("Installing @revstackhq/core...").start();
108
108
  let installFailed = false;
109
109
  try {
110
- const installArgs = packageManager === "yarn" ? ["add", "@revstackhq/core"] : packageManager === "pnpm" ? ["add", "@revstackhq/core"] : ["install", "@revstackhq/core"];
110
+ const pkgVersion = process.env.npm_package_version || "dev";
111
+ const tag = pkgVersion.includes("dev") ? "@dev" : "@latest";
112
+ const pkgName = `@revstackhq/core${tag}`;
113
+ const installArgs = packageManager === "yarn" ? ["add", pkgName] : packageManager === "pnpm" ? ["add", pkgName] : ["install", pkgName];
111
114
  let result = spawnSync(packageManager, installArgs, { cwd, shell: true });
112
115
  if (result.error || result.status !== 0) {
113
116
  if (packageManager === "pnpm") {
114
- result = spawnSync("pnpm", ["add", "-w", "@revstackhq/core"], {
117
+ result = spawnSync("pnpm", ["add", "-w", pkgName], {
115
118
  cwd,
116
119
  shell: true
117
120
  });
118
121
  } else if (packageManager === "yarn") {
119
- result = spawnSync("yarn", ["add", "-W", "@revstackhq/core"], {
122
+ result = spawnSync("yarn", ["add", "-W", pkgName], {
120
123
  cwd,
121
124
  shell: true
122
125
  });
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/commands/init.ts"],"sourcesContent":["/**\r\n * @file commands/init.ts\r\n * @description Scaffolds a new `revstack.config.ts` in the current directory.\r\n * Generates a starter config with the immutable Default Guest Plan and\r\n * a sample Pro plan using type-safe helpers from @revstackhq/core.\r\n */\r\n\r\nimport { Command } from \"commander\";\r\nimport chalk from \"chalk\";\r\nimport fs from \"node:fs\";\r\nimport path from \"node:path\";\r\nimport { spawnSync } from \"node:child_process\";\r\nimport ora from \"ora\";\r\n\r\nconst STARTER_FEATURES = `import { defineFeature } from \"@revstackhq/core\";\r\n\r\nexport const features = {\r\n seats: defineFeature({\r\n name: \"Seats\",\r\n type: \"static\",\r\n unit_type: \"count\",\r\n }),\r\n ai_tokens: defineFeature({\r\n name: \"AI Tokens\",\r\n type: \"metered\",\r\n unit_type: \"count\",\r\n }),\r\n};\r\n`;\r\n\r\nconst STARTER_PLANS = `import { definePlan } from \"@revstackhq/core\";\r\nimport { features } from \"./features\";\r\n\r\nexport const plans = {\r\n // DO NOT DELETE: Automatically created default plan for guests.\r\n default: definePlan<typeof features>({\r\n name: \"Default\",\r\n description: \"Automatically created default plan for guests.\",\r\n is_default: true,\r\n is_public: false,\r\n type: \"free\",\r\n features: {},\r\n }),\r\n pro: definePlan<typeof features>({\r\n name: \"Pro\",\r\n description: \"For professional teams.\",\r\n is_default: false,\r\n is_public: true,\r\n type: \"paid\",\r\n prices: [\r\n {\r\n amount: 2900,\r\n currency: \"USD\",\r\n billing_interval: \"monthly\",\r\n trial_period_days: 14,\r\n },\r\n {\r\n amount: 29000,\r\n currency: \"USD\",\r\n billing_interval: \"yearly\",\r\n trial_period_days: 14,\r\n }\r\n ],\r\n features: {\r\n seats: { value_limit: 5, is_hard_limit: true },\r\n ai_tokens: { value_limit: 1000, reset_period: \"monthly\" },\r\n },\r\n }),\r\n};\r\n`;\r\n\r\nconst STARTER_CONFIG = `import { defineConfig } from \"@revstackhq/core\";\r\nimport { features } from \"./revstack/features\";\r\nimport { plans } from \"./revstack/plans\";\r\n\r\nexport default defineConfig({\r\n features,\r\n plans,\r\n});\r\n`;\r\n\r\nexport const initCommand = new Command(\"init\")\r\n .description(\"Scaffold a new revstack.config.ts in the current directory\")\r\n .action(async () => {\r\n const cwd = process.cwd();\r\n const configPath = path.resolve(cwd, \"revstack.config.ts\");\r\n const revstackDir = path.resolve(cwd, \"revstack\");\r\n const featuresPath = path.resolve(revstackDir, \"features.ts\");\r\n const plansPath = path.resolve(revstackDir, \"plans.ts\");\r\n\r\n if (fs.existsSync(configPath)) {\r\n console.log(\r\n \"\\n\" +\r\n chalk.yellow(\" ⚠ revstack.config.ts already exists.\\n\") +\r\n chalk.dim(\" Delete it first if you want to start fresh.\\n\"),\r\n );\r\n process.exit(1);\r\n }\r\n\r\n // Step 1: Create revstack directory and files\r\n if (!fs.existsSync(revstackDir)) {\r\n fs.mkdirSync(revstackDir, { recursive: true });\r\n }\r\n fs.writeFileSync(featuresPath, STARTER_FEATURES, \"utf-8\");\r\n fs.writeFileSync(plansPath, STARTER_PLANS, \"utf-8\");\r\n fs.writeFileSync(configPath, STARTER_CONFIG, \"utf-8\");\r\n\r\n // Step 2: Detect package manager & verify package.json\r\n let packageManager = \"npm\";\r\n if (fs.existsSync(path.resolve(cwd, \"pnpm-lock.yaml\"))) {\r\n packageManager = \"pnpm\";\r\n } else if (fs.existsSync(path.resolve(cwd, \"yarn.lock\"))) {\r\n packageManager = \"yarn\";\r\n } else if (fs.existsSync(path.resolve(cwd, \"package-lock.json\"))) {\r\n packageManager = \"npm\";\r\n }\r\n\r\n const packageJsonPath = path.resolve(cwd, \"package.json\");\r\n if (!fs.existsSync(packageJsonPath)) {\r\n // Create a default package.json if it doesn't exist\r\n try {\r\n spawnSync(\"npm\", [\"init\", \"-y\"], { cwd, stdio: \"ignore\", shell: true });\r\n } catch (err) {\r\n // Ignore initialization errors; the install command may still work or provide a better error.\r\n }\r\n }\r\n\r\n // Step 3: Install @revstackhq/core\r\n const spinner = ora(\"Installing @revstackhq/core...\").start();\r\n let installFailed = false;\r\n\r\n try {\r\n const installArgs =\r\n packageManager === \"yarn\"\r\n ? [\"add\", \"@revstackhq/core\"]\r\n : packageManager === \"pnpm\"\r\n ? [\"add\", \"@revstackhq/core\"]\r\n : [\"install\", \"@revstackhq/core\"];\r\n\r\n let result = spawnSync(packageManager, installArgs, { cwd, shell: true });\r\n if (result.error || result.status !== 0) {\r\n if (packageManager === \"pnpm\") {\r\n result = spawnSync(\"pnpm\", [\"add\", \"-w\", \"@revstackhq/core\"], {\r\n cwd,\r\n shell: true,\r\n });\r\n } else if (packageManager === \"yarn\") {\r\n result = spawnSync(\"yarn\", [\"add\", \"-W\", \"@revstackhq/core\"], {\r\n cwd,\r\n shell: true,\r\n });\r\n }\r\n }\r\n\r\n if (result.error || result.status !== 0) {\r\n throw new Error(\r\n \"Install failed: \" +\r\n (result.stderr\r\n ? result.stderr.toString()\r\n : result.stdout\r\n ? result.stdout.toString()\r\n : \"Unknown error\"),\r\n );\r\n }\r\n spinner.succeed(\"Dependencies installed\");\r\n } catch (err: any) {\r\n installFailed = true;\r\n spinner.fail(\r\n \"Failed to install @revstackhq/core automatically (\" +\r\n packageManager +\r\n \"). Reason: \" +\r\n err.message,\r\n );\r\n }\r\n\r\n // Step 4: Final Success Message\r\n console.log(\r\n \"\\n\" +\r\n chalk.green(\" ✔ Created revstack config structure\\n\") +\r\n \"\\n\" +\r\n chalk.dim(\" Includes the \") +\r\n chalk.white(\"Default Guest Plan\") +\r\n chalk.dim(\" (required by Revstack).\\n\") +\r\n \"\\\\n\" +\r\n chalk.dim(\" Next steps:\\\\n\") +\r\n (installFailed\r\n ? chalk.dim(\" 0. \") +\r\n chalk.white(\r\n \"Run \" +\r\n chalk.bold(packageManager + \" install @revstackhq/core\") +\r\n \" manually\\\\n\",\r\n )\r\n : \"\") +\r\n chalk.dim(\" 1. \") +\r\n chalk.white(\"Edit the config to match your billing model\\\\n\") +\r\n chalk.dim(\" 2. \") +\r\n chalk.white(\"Run \") +\r\n chalk.bold(\"revstack login\") +\r\n chalk.white(\" to authenticate\\\\n\") +\r\n chalk.dim(\" 3. \") +\r\n chalk.white(\"Run \") +\r\n chalk.bold(\"revstack push\") +\r\n chalk.white(\" to deploy\\\\n\"),\r\n );\r\n });\r\n"],"mappings":"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","names":[]}
1
+ {"version":3,"sources":["../../src/commands/init.ts"],"sourcesContent":["/**\r\n * @file commands/init.ts\r\n * @description Scaffolds a new `revstack.config.ts` in the current directory.\r\n * Generates a starter config with the immutable Default Guest Plan and\r\n * a sample Pro plan using type-safe helpers from @revstackhq/core.\r\n */\r\n\r\nimport { Command } from \"commander\";\r\nimport chalk from \"chalk\";\r\nimport fs from \"node:fs\";\r\nimport path from \"node:path\";\r\nimport { spawnSync } from \"node:child_process\";\r\nimport ora from \"ora\";\r\n\r\nconst STARTER_FEATURES = `import { defineFeature } from \"@revstackhq/core\";\r\n\r\nexport const features = {\r\n seats: defineFeature({\r\n name: \"Seats\",\r\n type: \"static\",\r\n unit_type: \"count\",\r\n }),\r\n ai_tokens: defineFeature({\r\n name: \"AI Tokens\",\r\n type: \"metered\",\r\n unit_type: \"count\",\r\n }),\r\n};\r\n`;\r\n\r\nconst STARTER_PLANS = `import { definePlan } from \"@revstackhq/core\";\r\nimport { features } from \"./features\";\r\n\r\nexport const plans = {\r\n // DO NOT DELETE: Automatically created default plan for guests.\r\n default: definePlan<typeof features>({\r\n name: \"Default\",\r\n description: \"Automatically created default plan for guests.\",\r\n is_default: true,\r\n is_public: false,\r\n type: \"free\",\r\n features: {},\r\n }),\r\n pro: definePlan<typeof features>({\r\n name: \"Pro\",\r\n description: \"For professional teams.\",\r\n is_default: false,\r\n is_public: true,\r\n type: \"paid\",\r\n prices: [\r\n {\r\n amount: 2900,\r\n currency: \"USD\",\r\n billing_interval: \"monthly\",\r\n trial_period_days: 14,\r\n },\r\n {\r\n amount: 29000,\r\n currency: \"USD\",\r\n billing_interval: \"yearly\",\r\n trial_period_days: 14,\r\n }\r\n ],\r\n features: {\r\n seats: { value_limit: 5, is_hard_limit: true },\r\n ai_tokens: { value_limit: 1000, reset_period: \"monthly\" },\r\n },\r\n }),\r\n};\r\n`;\r\n\r\nconst STARTER_CONFIG = `import { defineConfig } from \"@revstackhq/core\";\r\nimport { features } from \"./revstack/features\";\r\nimport { plans } from \"./revstack/plans\";\r\n\r\nexport default defineConfig({\r\n features,\r\n plans,\r\n});\r\n`;\r\n\r\nexport const initCommand = new Command(\"init\")\r\n .description(\"Scaffold a new revstack.config.ts in the current directory\")\r\n .action(async () => {\r\n const cwd = process.cwd();\r\n const configPath = path.resolve(cwd, \"revstack.config.ts\");\r\n const revstackDir = path.resolve(cwd, \"revstack\");\r\n const featuresPath = path.resolve(revstackDir, \"features.ts\");\r\n const plansPath = path.resolve(revstackDir, \"plans.ts\");\r\n\r\n if (fs.existsSync(configPath)) {\r\n console.log(\r\n \"\\n\" +\r\n chalk.yellow(\" ⚠ revstack.config.ts already exists.\\n\") +\r\n chalk.dim(\" Delete it first if you want to start fresh.\\n\"),\r\n );\r\n process.exit(1);\r\n }\r\n\r\n // Step 1: Create revstack directory and files\r\n if (!fs.existsSync(revstackDir)) {\r\n fs.mkdirSync(revstackDir, { recursive: true });\r\n }\r\n fs.writeFileSync(featuresPath, STARTER_FEATURES, \"utf-8\");\r\n fs.writeFileSync(plansPath, STARTER_PLANS, \"utf-8\");\r\n fs.writeFileSync(configPath, STARTER_CONFIG, \"utf-8\");\r\n\r\n // Step 2: Detect package manager & verify package.json\r\n let packageManager = \"npm\";\r\n if (fs.existsSync(path.resolve(cwd, \"pnpm-lock.yaml\"))) {\r\n packageManager = \"pnpm\";\r\n } else if (fs.existsSync(path.resolve(cwd, \"yarn.lock\"))) {\r\n packageManager = \"yarn\";\r\n } else if (fs.existsSync(path.resolve(cwd, \"package-lock.json\"))) {\r\n packageManager = \"npm\";\r\n }\r\n\r\n const packageJsonPath = path.resolve(cwd, \"package.json\");\r\n if (!fs.existsSync(packageJsonPath)) {\r\n // Create a default package.json if it doesn't exist\r\n try {\r\n spawnSync(\"npm\", [\"init\", \"-y\"], { cwd, stdio: \"ignore\", shell: true });\r\n } catch (err) {\r\n // Ignore initialization errors; the install command may still work or provide a better error.\r\n }\r\n }\r\n\r\n // Step 3: Install @revstackhq/core\r\n const spinner = ora(\"Installing @revstackhq/core...\").start();\r\n let installFailed = false;\r\n\r\n try {\r\n // Use @dev tag if the CLI itself is a dev snapshot\r\n const pkgVersion = process.env.npm_package_version || \"dev\";\r\n const tag = pkgVersion.includes(\"dev\") ? \"@dev\" : \"@latest\";\r\n const pkgName = `@revstackhq/core${tag}`;\r\n\r\n const installArgs =\r\n packageManager === \"yarn\"\r\n ? [\"add\", pkgName]\r\n : packageManager === \"pnpm\"\r\n ? [\"add\", pkgName]\r\n : [\"install\", pkgName];\r\n\r\n let result = spawnSync(packageManager, installArgs, { cwd, shell: true });\r\n if (result.error || result.status !== 0) {\r\n if (packageManager === \"pnpm\") {\r\n result = spawnSync(\"pnpm\", [\"add\", \"-w\", pkgName], {\r\n cwd,\r\n shell: true,\r\n });\r\n } else if (packageManager === \"yarn\") {\r\n result = spawnSync(\"yarn\", [\"add\", \"-W\", pkgName], {\r\n cwd,\r\n shell: true,\r\n });\r\n }\r\n }\r\n\r\n if (result.error || result.status !== 0) {\r\n throw new Error(\r\n \"Install failed: \" +\r\n (result.stderr\r\n ? result.stderr.toString()\r\n : result.stdout\r\n ? result.stdout.toString()\r\n : \"Unknown error\"),\r\n );\r\n }\r\n spinner.succeed(\"Dependencies installed\");\r\n } catch (err: any) {\r\n installFailed = true;\r\n spinner.fail(\r\n \"Failed to install @revstackhq/core automatically (\" +\r\n packageManager +\r\n \"). Reason: \" +\r\n err.message,\r\n );\r\n }\r\n\r\n // Step 4: Final Success Message\r\n console.log(\r\n \"\\n\" +\r\n chalk.green(\" ✔ Created revstack config structure\\n\") +\r\n \"\\n\" +\r\n chalk.dim(\" Includes the \") +\r\n chalk.white(\"Default Guest Plan\") +\r\n chalk.dim(\" (required by Revstack).\\n\") +\r\n \"\\\\n\" +\r\n chalk.dim(\" Next steps:\\\\n\") +\r\n (installFailed\r\n ? chalk.dim(\" 0. \") +\r\n chalk.white(\r\n \"Run \" +\r\n chalk.bold(packageManager + \" install @revstackhq/core\") +\r\n \" manually\\\\n\",\r\n )\r\n : \"\") +\r\n chalk.dim(\" 1. \") +\r\n chalk.white(\"Edit the config to match your billing model\\\\n\") +\r\n chalk.dim(\" 2. \") +\r\n chalk.white(\"Run \") +\r\n chalk.bold(\"revstack login\") +\r\n chalk.white(\" to authenticate\\\\n\") +\r\n chalk.dim(\" 3. \") +\r\n chalk.white(\"Run \") +\r\n chalk.bold(\"revstack push\") +\r\n chalk.white(\" to deploy\\\\n\"),\r\n );\r\n });\r\n"],"mappings":"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","names":[]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@revstackhq/cli",
3
- "version": "0.0.0-dev-20260226061807",
3
+ "version": "0.0.0-dev-20260226063200",
4
4
  "description": "The official CLI for Revstack — Billing as Code",
5
5
  "license": "MIT",
6
6
  "type": "module",
@@ -130,22 +130,27 @@ export const initCommand = new Command("init")
130
130
  let installFailed = false;
131
131
 
132
132
  try {
133
+ // Use @dev tag if the CLI itself is a dev snapshot
134
+ const pkgVersion = process.env.npm_package_version || "dev";
135
+ const tag = pkgVersion.includes("dev") ? "@dev" : "@latest";
136
+ const pkgName = `@revstackhq/core${tag}`;
137
+
133
138
  const installArgs =
134
139
  packageManager === "yarn"
135
- ? ["add", "@revstackhq/core"]
140
+ ? ["add", pkgName]
136
141
  : packageManager === "pnpm"
137
- ? ["add", "@revstackhq/core"]
138
- : ["install", "@revstackhq/core"];
142
+ ? ["add", pkgName]
143
+ : ["install", pkgName];
139
144
 
140
145
  let result = spawnSync(packageManager, installArgs, { cwd, shell: true });
141
146
  if (result.error || result.status !== 0) {
142
147
  if (packageManager === "pnpm") {
143
- result = spawnSync("pnpm", ["add", "-w", "@revstackhq/core"], {
148
+ result = spawnSync("pnpm", ["add", "-w", pkgName], {
144
149
  cwd,
145
150
  shell: true,
146
151
  });
147
152
  } else if (packageManager === "yarn") {
148
- result = spawnSync("yarn", ["add", "-W", "@revstackhq/core"], {
153
+ result = spawnSync("yarn", ["add", "-W", pkgName], {
149
154
  cwd,
150
155
  shell: true,
151
156
  });