kasy-cli 1.3.0 → 1.3.2
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 +1 -1
- package/lib/utils/i18n.js +3 -0
- package/lib/utils/license-gate.js +40 -15
- package/package.json +1 -1
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 &&
|
|
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
|
|
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,41 @@ 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
|
-
|
|
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
|
-
|
|
104
|
-
|
|
105
|
-
|
|
120
|
+
if (result.reason === 'inactive') {
|
|
121
|
+
// Admin deactivated this key (fraud/chargeback) — clear it so user can enter a new one
|
|
122
|
+
setStoredLicenseKey('');
|
|
123
|
+
throw new Error(t('license.inactive'));
|
|
106
124
|
}
|
|
107
|
-
|
|
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
|
-
setStoredLicenseKey('');
|
|
112
|
-
setCachedValidation({ valid: false });
|
|
125
|
+
// Not found: keep the stored key (might not be in DB yet), never cache failures
|
|
113
126
|
throw new Error(t('license.invalid'));
|
|
114
127
|
}
|
|
115
128
|
|
|
116
|
-
//
|
|
129
|
+
// Key valid — save cache
|
|
117
130
|
setStoredLicenseKey(key);
|
|
118
|
-
setCachedValidation({
|
|
131
|
+
setCachedValidation({
|
|
132
|
+
valid: true,
|
|
133
|
+
subscription_active: result.subscription_active,
|
|
134
|
+
plan: result.plan,
|
|
135
|
+
expires_at: result.expires_at,
|
|
136
|
+
});
|
|
137
|
+
|
|
119
138
|
console.log(kleur.green('✓'));
|
|
120
139
|
console.log('');
|
|
140
|
+
|
|
141
|
+
// Check subscription for update commands
|
|
142
|
+
if (needsActiveSubscription && !result.subscription_active) {
|
|
143
|
+
throw new Error(t('license.subscriptionExpired'));
|
|
144
|
+
}
|
|
145
|
+
|
|
121
146
|
return key;
|
|
122
147
|
}
|
|
123
148
|
|