rampup 0.1.8 → 0.1.10

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 (3) hide show
  1. package/entitlements.js +3 -2
  2. package/index.js +101 -31
  3. package/package.json +1 -1
package/entitlements.js CHANGED
@@ -8,12 +8,13 @@ import { getIdToken } from './auth.js';
8
8
  const ENTITLEMENT_API_URL = process.env.ENTITLEMENT_API_URL ||
9
9
  'https://entitlement-service.rian-19c.workers.dev';
10
10
 
11
- // Action costs mapping
11
+ // Action costs mapping (must match token_costs in entitlement service)
12
12
  const ACTION_COSTS = {
13
13
  'learn': { action: 'chat', credits: 1 },
14
14
  'ask': { action: 'chat', credits: 1 },
15
15
  'guide': { action: 'generate', credits: 2 },
16
- 'voice': { action: 'chat', credits: 2 },
16
+ 'voice': { action: 'voice', credits: 2 }, // Per voice interaction
17
+ 'voice_session': { action: 'voice', credits: 2 }, // Session start
17
18
  'architect': { action: 'debug', credits: 3 },
18
19
  };
19
20
 
package/index.js CHANGED
@@ -739,6 +739,17 @@ program
739
739
  // Get fresh token after potential login
740
740
  const authToken = await getIdToken();
741
741
 
742
+ // Check entitlements before starting voice session
743
+ const idempotencyKey = `voice-${Date.now()}`;
744
+ const entitlementCheck = await checkAndBurnTokens('voice_session', idempotencyKey);
745
+ if (!entitlementCheck.allowed) {
746
+ console.log(chalk.red(`\n❌ ${entitlementCheck.reason}\n`));
747
+ process.exit(1);
748
+ }
749
+ if (entitlementCheck.balance !== undefined) {
750
+ console.log(chalk.dim(`Credits remaining: ${entitlementCheck.balance}\n`));
751
+ }
752
+
742
753
  const RAMP_API_URL = process.env.RAMP_API_URL || 'https://ramp-api-946191982468.us-central1.run.app';
743
754
 
744
755
  // Track usage
@@ -1375,47 +1386,106 @@ program
1375
1386
  console.log(banner);
1376
1387
  console.log(chalk.bold.blue('⚡ Upgrade Plan\n'));
1377
1388
 
1378
- // Check current usage
1379
- const usageFile = path.join(process.env.HOME, '.ramp', 'voice-usage.json');
1380
- let usage = { totalMinutes: 0 };
1389
+ const ENTITLEMENT_API_URL = process.env.ENTITLEMENT_API_URL ||
1390
+ 'https://entitlement-service.rian-19c.workers.dev';
1391
+
1392
+ // Check if user is logged in
1393
+ const token = await getIdToken();
1394
+ if (!token) {
1395
+ console.log(chalk.yellow('Please log in first to upgrade your plan.\n'));
1396
+ console.log(chalk.dim('Run: ramp login\n'));
1397
+ return;
1398
+ }
1399
+
1400
+ const spinner = ora('Loading plans...').start();
1401
+
1381
1402
  try {
1382
- usage = JSON.parse(await fs.readFile(usageFile, 'utf8'));
1383
- } catch {}
1403
+ // Get current balance
1404
+ const balance = await getTokenBalance();
1405
+ const currentCredits = balance?.balances?.[0]?.balance || 0;
1384
1406
 
1385
- console.log(chalk.bold('Current: Free Tier'));
1386
- console.log(chalk.dim(` Voice: ${usage.totalMinutes.toFixed(2)} / 10 min used\n`));
1407
+ // Fetch plans from API
1408
+ const plansResponse = await fetch(`${ENTITLEMENT_API_URL}/billing/ramp/plans`);
1409
+ if (!plansResponse.ok) {
1410
+ throw new Error('Failed to fetch plans');
1411
+ }
1412
+ const { plans } = await plansResponse.json();
1387
1413
 
1388
- console.log(chalk.bold('Available Plans:\n'));
1414
+ spinner.succeed('Plans loaded!\n');
1389
1415
 
1390
- console.log(chalk.cyan(' Starter - $29/mo'));
1391
- console.log(chalk.dim(' 100 voice minutes'));
1392
- console.log(chalk.dim(' • Unlimited text queries'));
1393
- console.log(chalk.dim(' • 5 team members\n'));
1416
+ // Show current balance
1417
+ console.log(chalk.bold('Current Balance:'), chalk.cyan(`${currentCredits} credits\n`));
1394
1418
 
1395
- console.log(chalk.cyan(' Pro - $99/mo'));
1396
- console.log(chalk.dim(' • 500 voice minutes'));
1397
- console.log(chalk.dim(' • Unlimited everything'));
1398
- console.log(chalk.dim(' • 25 team members'));
1399
- console.log(chalk.dim(' • Priority support\n'));
1419
+ console.log(chalk.bold('Available Plans:\n'));
1400
1420
 
1401
- console.log(chalk.cyan(' Enterprise - Custom'));
1402
- console.log(chalk.dim(' • Unlimited voice minutes'));
1403
- console.log(chalk.dim(' • Unlimited team members'));
1404
- console.log(chalk.dim(' • SSO, audit logs'));
1405
- console.log(chalk.dim(' • Dedicated support\n'));
1421
+ // Display plans
1422
+ for (const plan of plans) {
1423
+ const priceStr = plan.price === 0 ? 'Free' : `$${plan.price}/mo`;
1424
+ const label = plan.popular ? `${plan.name} ⭐` : plan.name;
1425
+ console.log(chalk.cyan(` ${label} - ${priceStr}`));
1426
+ for (const feature of plan.features) {
1427
+ console.log(chalk.dim(` • ${feature}`));
1428
+ }
1429
+ console.log();
1430
+ }
1406
1431
 
1407
- console.log(chalk.dim('Upgrade at: https://rampup.dev/pricing\n'));
1432
+ // Get paid plans for selection
1433
+ const paidPlans = plans.filter(p => p.price > 0);
1408
1434
 
1409
- const { openBrowser } = await inquirer.prompt([{
1410
- type: 'confirm',
1411
- name: 'openBrowser',
1412
- message: 'Open pricing page?',
1413
- default: true
1414
- }]);
1435
+ const { selectedPlan } = await inquirer.prompt([{
1436
+ type: 'list',
1437
+ name: 'selectedPlan',
1438
+ message: 'Select a plan to upgrade:',
1439
+ choices: [
1440
+ ...paidPlans.map(p => ({
1441
+ name: `${p.name} ($${p.price}/mo) - ${p.credits} credits`,
1442
+ value: p.id
1443
+ })),
1444
+ { name: 'Cancel', value: null }
1445
+ ]
1446
+ }]);
1415
1447
 
1416
- if (openBrowser) {
1448
+ if (!selectedPlan) {
1449
+ console.log(chalk.dim('\nNo plan selected.\n'));
1450
+ return;
1451
+ }
1452
+
1453
+ // Create checkout session
1454
+ const checkoutSpinner = ora('Creating checkout session...').start();
1455
+
1456
+ const checkoutResponse = await fetch(`${ENTITLEMENT_API_URL}/billing/ramp/checkout`, {
1457
+ method: 'POST',
1458
+ headers: {
1459
+ 'Authorization': `Bearer ${token}`,
1460
+ 'Content-Type': 'application/json'
1461
+ },
1462
+ body: JSON.stringify({
1463
+ plan: selectedPlan,
1464
+ successUrl: 'https://rampup.dev/billing?checkout=success',
1465
+ cancelUrl: 'https://rampup.dev/pricing?checkout=cancelled'
1466
+ })
1467
+ });
1468
+
1469
+ if (!checkoutResponse.ok) {
1470
+ const error = await checkoutResponse.json().catch(() => ({}));
1471
+ throw new Error(error.message || 'Failed to create checkout session');
1472
+ }
1473
+
1474
+ const { url } = await checkoutResponse.json();
1475
+ checkoutSpinner.succeed('Checkout ready!\n');
1476
+
1477
+ console.log(chalk.green('Opening Stripe checkout in your browser...\n'));
1417
1478
  const open = (await import('open')).default;
1418
- await open('https://rampup.dev/pricing');
1479
+ await open(url);
1480
+
1481
+ console.log(chalk.dim('If browser doesn\'t open, visit:'));
1482
+ console.log(chalk.cyan(url));
1483
+ console.log();
1484
+
1485
+ } catch (error) {
1486
+ spinner.fail('Failed to load upgrade options');
1487
+ console.error(chalk.red(error.message));
1488
+ console.log(chalk.dim('\nYou can also upgrade at: https://rampup.dev/pricing\n'));
1419
1489
  }
1420
1490
  });
1421
1491
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "rampup",
3
- "version": "0.1.8",
3
+ "version": "0.1.10",
4
4
  "description": "Ramp - Understand any codebase in hours. AI-powered developer onboarding CLI.",
5
5
  "type": "module",
6
6
  "bin": {