kasy-cli 1.3.0 → 1.3.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/bin/kasy.js CHANGED
@@ -516,7 +516,7 @@ async function main() {
516
516
  const argv = process.argv.slice(2);
517
517
  const language = await resolveLanguage(argv);
518
518
  if (shouldRequireLicenseForArgv(argv)) {
519
- await ensureLicenseKey({ language });
519
+ await ensureLicenseKey({ language, argv });
520
520
  }
521
521
  await checkForUpdates();
522
522
  const program = buildProgram(language);
package/lib/utils/i18n.js CHANGED
@@ -108,6 +108,7 @@ const MESSAGES = {
108
108
  'license.expired': '❌ Your license has expired. Renew at kasy.dev.',
109
109
  'license.inactive': '❌ Your license has been deactivated. Contact support at kasy.dev.',
110
110
  'license.offlineWarning': '⚠️ Could not reach license server — continuing offline.',
111
+ 'license.subscriptionExpired': '❌ Your subscription has expired. Updates require an active plan. Renew at kasy.dev.\n Your existing projects still work — only updates are locked.',
111
112
  'prompt.license.enter': '👉 Enter your license key (XXXX-XXXX-XXXX-XXXX)',
112
113
  'prompt.license.invalid': 'Invalid format. Use XXXX-XXXX-XXXX-XXXX.',
113
114
  'prompt.appName.enter': 'Enter the name of your app',
@@ -796,6 +797,7 @@ const MESSAGES = {
796
797
  'license.expired': '❌ Sua assinatura expirou. Renove em kasy.dev.',
797
798
  'license.inactive': '❌ Sua chave foi desativada. Entre em contato em kasy.dev.',
798
799
  'license.offlineWarning': '⚠️ Servidor fora do ar — continuando no modo offline.',
800
+ 'license.subscriptionExpired': '❌ Sua assinatura expirou. Atualizacoes exigem plano ativo. Renove em kasy.dev.\n Seus projetos continuam funcionando — apenas atualizacoes estao bloqueadas.',
799
801
  'prompt.license.enter': '👉 Digite sua chave de ativação (XXXX-XXXX-XXXX-XXXX)',
800
802
  'prompt.license.invalid': 'Formato inválido. Use XXXX-XXXX-XXXX-XXXX.',
801
803
  'prompt.appName.enter': 'Digite o nome do seu app',
@@ -1484,6 +1486,7 @@ const MESSAGES = {
1484
1486
  'license.expired': '❌ Tu suscripción ha expirado. Renueva en kasy.dev.',
1485
1487
  'license.inactive': '❌ Tu clave fue desactivada. Contacta soporte en kasy.dev.',
1486
1488
  'license.offlineWarning': '⚠️ No se pudo conectar al servidor — continuando sin conexión.',
1489
+ 'license.subscriptionExpired': '❌ Tu suscripcion expiró. Las actualizaciones requieren plan activo. Renueva en kasy.dev.\n Tus proyectos siguen funcionando — solo las actualizaciones están bloqueadas.',
1487
1490
  'prompt.license.enter': '👉 Ingresa tu clave de activación (XXXX-XXXX-XXXX-XXXX)',
1488
1491
  'prompt.license.invalid': 'Formato inválido. Usa XXXX-XXXX-XXXX-XXXX.',
1489
1492
  'prompt.appName.enter': 'Ingresa el nombre de tu app',
@@ -9,8 +9,15 @@ const { createTranslator, detectDefaultLanguage } = require('./i18n');
9
9
  const VALIDATE_URL = 'https://xkefozfsrmqjtesiitvk.supabase.co/functions/v1/validate-license';
10
10
  const CACHE_TTL_MS = 7 * 24 * 60 * 60 * 1000; // 7 days
11
11
 
12
+ // No key needed
12
13
  const FREE_COMMANDS = new Set(['run', 'doctor', 'features', 'version', 'help', 'uninstall']);
13
14
 
15
+ // Requires key — even if subscription expired (you bought it, it's yours forever)
16
+ const PREMIUM_COMMANDS = new Set(['new', 'setup', 'add', 'remove', 'deploy', 'check', 'validate', 'docs', 'ios', 'codemagic', 'notifications']);
17
+
18
+ // Requires key AND active subscription (update/upgrade only while plan is current)
19
+ const UPDATE_COMMANDS = new Set(['upgrade', 'update']);
20
+
14
21
  function resolveCommandFromArgv(argv = []) {
15
22
  for (let i = 0; i < argv.length; i += 1) {
16
23
  const arg = argv[i];
@@ -24,7 +31,12 @@ function resolveCommandFromArgv(argv = []) {
24
31
 
25
32
  function shouldRequireLicenseForArgv(argv = []) {
26
33
  const command = resolveCommandFromArgv(argv);
27
- return !!command && !FREE_COMMANDS.has(command);
34
+ return !!command && (PREMIUM_COMMANDS.has(command) || UPDATE_COMMANDS.has(command));
35
+ }
36
+
37
+ function requiresActiveSubscription(argv = []) {
38
+ const command = resolveCommandFromArgv(argv);
39
+ return !!command && UPDATE_COMMANDS.has(command);
28
40
  }
29
41
 
30
42
  function validateOnServer(key) {
@@ -69,6 +81,7 @@ function setCachedValidation(result) {
69
81
  async function ensureLicenseKey(options = {}) {
70
82
  const language = options.language || detectDefaultLanguage();
71
83
  const t = createTranslator(language);
84
+ const needsActiveSubscription = requiresActiveSubscription(options.argv || []);
72
85
 
73
86
  let key = getStoredLicenseKey();
74
87
 
@@ -82,9 +95,12 @@ async function ensureLicenseKey(options = {}) {
82
95
  if (!isLicenseFormatValid(key)) throw new Error(t('license.invalid'));
83
96
  }
84
97
 
85
- // Check local cache first (avoids network on every command)
98
+ // Check local cache (avoids network on every command)
86
99
  const cached = getCachedValidation();
87
100
  if (cached && cached.valid) {
101
+ if (needsActiveSubscription && !cached.subscription_active) {
102
+ throw new Error(t('license.subscriptionExpired'));
103
+ }
88
104
  return key;
89
105
  }
90
106
 
@@ -92,32 +108,40 @@ async function ensureLicenseKey(options = {}) {
92
108
  process.stdout.write(kleur.cyan(t('license.checking')) + ' ');
93
109
  const result = await validateOnServer(key);
94
110
 
111
+ // Network error — offline tolerance
95
112
  if (!result) {
96
- // Network error — allow if key format is valid (offline tolerance)
97
- console.log(kleur.yellow(t('license.offlineWarning') || '(offline — using local key)'));
113
+ console.log(kleur.yellow(t('license.offlineWarning')));
98
114
  console.log('');
99
115
  return key;
100
116
  }
101
117
 
118
+ // Key not found or deactivated
102
119
  if (!result.valid) {
103
- const reason = result.reason;
104
- if (reason === 'expired') {
105
- throw new Error(t('license.expired') || 'Your license has expired. Renew at kasy.dev.');
106
- }
107
- if (reason === 'inactive') {
108
- throw new Error(t('license.inactive') || 'Your license has been deactivated. Contact support at kasy.dev.');
109
- }
110
- // Key not found — clear stored key so user can re-enter
111
120
  setStoredLicenseKey('');
112
121
  setCachedValidation({ valid: false });
122
+ if (result.reason === 'inactive') {
123
+ throw new Error(t('license.inactive'));
124
+ }
113
125
  throw new Error(t('license.invalid'));
114
126
  }
115
127
 
116
- // Valid — save key and cache result
128
+ // Key valid — save cache
117
129
  setStoredLicenseKey(key);
118
- setCachedValidation({ valid: true, plan: result.plan, expires_at: result.expires_at });
130
+ setCachedValidation({
131
+ valid: true,
132
+ subscription_active: result.subscription_active,
133
+ plan: result.plan,
134
+ expires_at: result.expires_at,
135
+ });
136
+
119
137
  console.log(kleur.green('✓'));
120
138
  console.log('');
139
+
140
+ // Check subscription for update commands
141
+ if (needsActiveSubscription && !result.subscription_active) {
142
+ throw new Error(t('license.subscriptionExpired'));
143
+ }
144
+
121
145
  return key;
122
146
  }
123
147
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "kasy-cli",
3
- "version": "1.3.0",
3
+ "version": "1.3.1",
4
4
  "description": "CLI for scaffolding production-ready Flutter SaaS apps with Firebase, Supabase, or API REST backends.",
5
5
  "bin": {
6
6
  "kasy": "./bin/kasy.js"