kasy-cli 1.3.1 → 1.4.0

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.
@@ -58,6 +58,16 @@ async function waitWithCountdown(seconds, label) {
58
58
  }
59
59
  process.stdout.write(`\r ${label} pronto! \n`);
60
60
  }
61
+
62
+ function openUrl(url) {
63
+ try {
64
+ const { exec } = require('node:child_process');
65
+ const cmd = process.platform === 'darwin' ? `open "${url}"`
66
+ : process.platform === 'win32' ? `start "" "${url}"`
67
+ : `xdg-open "${url}"`;
68
+ exec(cmd, { shell: true });
69
+ } catch (_) {}
70
+ }
61
71
  const prompts = require('prompts');
62
72
  const fs = require('fs-extra');
63
73
  const { createTranslator } = require('../utils/i18n');
@@ -334,9 +344,9 @@ const STEP_LABELS = {
334
344
 
335
345
  const STEP_PROGRESS = {
336
346
  'project-setup': { en: 'Configuring your project…', pt: 'Configurando seu projeto…', es: 'Configurando tu proyecto…' },
337
- 'pub-get': { en: 'Installing packages…', pt: 'Instalando pacotes…', es: 'Instalando paquetes…' },
338
- 'slang': { en: 'Generating translations…', pt: 'Gerando traducoes…', es: 'Generando traducciones…' },
339
- 'build-runner': { en: 'Generating code (Riverpod / Freezed)…', pt: 'Gerando codigo (pode demorar)', es: 'Generando codigo (puede tardar)' },
347
+ 'pub-get': { en: 'Installing packages… (may take a few minutes)', pt: 'Instalando pacotes… (pode levar alguns minutos)', es: 'Instalando paquetes… (puede tardar varios minutos)' },
348
+ 'slang': { en: 'Generating translations…', pt: 'Gerando traducoes…', es: 'Generando traducciones…' },
349
+ 'build-runner': { en: 'Generating code (Riverpod / Freezed)… (may take a few minutes)', pt: 'Gerando codigo (Riverpod / Freezed)… (pode demorar)', es: 'Generando codigo (Riverpod / Freezed)… (puede tardar)' },
340
350
  'flutterfire': { en: 'Connecting to Firebase…', pt: 'Conectando ao Firebase…', es: 'Conectando a Firebase…' },
341
351
  'deploy': { en: 'Deploying backend to Firebase…', pt: 'Publicando backend no Firebase…', es: 'Desplegando backend en Firebase…' },
342
352
  'gcp-project': { en: 'Creating GCP project…', pt: 'Criando projeto GCP…', es: 'Creando proyecto GCP…' },
@@ -372,31 +382,31 @@ function printCreateFromScratchStatus(result, tr) {
372
382
  if (result.sha1Skipped) {
373
383
  const err = (result.sha1Error || '').replace(/\s+/g, ' ').slice(0, 120);
374
384
  if (result.sha1Skipped === 'api_failed') {
375
- console.log(kleur.yellow(` SHA-1 não adicionado automaticamente. Motivo: ${err}`));
385
+ console.log(kleur.yellow(` ${tr('new.sha1.skipped.apiFailed', { error: err })}`));
376
386
  } else {
377
- console.log(kleur.yellow(` SHA-1 não adicionado: ${result.sha1Skipped}`));
387
+ console.log(kleur.yellow(` ${tr('new.sha1.skipped.other', { reason: result.sha1Skipped })}`));
378
388
  }
379
- console.log(kleur.yellow(` Adicione manualmente: Firebase Console → Configurações do projeto → Seus apps → Android → Adicionar impressão digital`));
389
+ console.log(kleur.yellow(` ${tr('new.sha1.addManually')}`));
380
390
  if (result.sha1ManualUrl) console.log(kleur.cyan(` ${result.sha1ManualUrl}`));
381
391
  } else {
382
- console.log(kleur.green(` ✓ SHA-1 adicionado (Google Sign-In)`));
392
+ console.log(kleur.green(` ${tr('new.sha1.added')}`));
383
393
  }
384
394
 
385
395
  if (result.firestoreCreated) {
386
- console.log(kleur.green(` ✓ Firestore criado automaticamente`));
396
+ console.log(kleur.green(` ${tr('new.firestore.created')}`));
387
397
  } else {
388
- if (result.firestoreError) console.log(kleur.yellow(` Firestore não criado. Motivo: ${(result.firestoreError || '').slice(0, 100)}`));
389
- else console.log(kleur.yellow(` ⚠ Firestore não criado automaticamente`));
390
- console.log(kleur.yellow(` Ative manualmente no console:`));
398
+ if (result.firestoreError) console.log(kleur.yellow(` ${tr('new.firestore.notCreated.error', { error: (result.firestoreError || '').slice(0, 100) })}`));
399
+ else console.log(kleur.yellow(` ${tr('new.firestore.notCreated')}`));
400
+ console.log(kleur.yellow(` ${tr('new.activateManually')}`));
391
401
  console.log(kleur.cyan(` ${result.firestoreUrl}`));
392
402
  }
393
403
 
394
404
  if (result.storageCreated) {
395
- console.log(kleur.green(` ✓ Firebase Storage criado automaticamente`));
405
+ console.log(kleur.green(` ${tr('new.storage.created')}`));
396
406
  } else {
397
- if (result.storageError) console.log(kleur.yellow(` Storage não criado. Motivo: ${(result.storageError || '').slice(0, 100)}`));
398
- else console.log(kleur.yellow(` ⚠ Storage não criado automaticamente`));
399
- console.log(kleur.yellow(` Ative manualmente no console:`));
407
+ if (result.storageError) console.log(kleur.yellow(` ${tr('new.storage.notCreated.error', { error: (result.storageError || '').slice(0, 100) })}`));
408
+ else console.log(kleur.yellow(` ${tr('new.storage.notCreated')}`));
409
+ console.log(kleur.yellow(` ${tr('new.activateManually')}`));
400
410
  console.log(kleur.cyan(` ${result.storageUrl}`));
401
411
  }
402
412
  }
@@ -419,12 +429,18 @@ function printSuccessCard(tr, answers, targetDir) {
419
429
  console.log(` ${kleur.dim('1.')} ${kleur.dim(tr('new.success.step.cd'))}`);
420
430
  console.log(` ${kleur.cyan(`cd ${folderName}`)}`);
421
431
  console.log('');
422
- console.log(` ${kleur.dim('2.')} ${kleur.dim(tr('new.success.step.run'))}`);
432
+ let stepNum = 2;
433
+ if (answers.backend === 'firebase') {
434
+ console.log(` ${kleur.dim(`${stepNum++}.`)} ${kleur.dim(tr('new.success.step.deploy'))}`);
435
+ console.log(` ${kleur.cyan('kasy deploy')}`);
436
+ console.log('');
437
+ }
438
+ console.log(` ${kleur.dim(`${stepNum++}.`)} ${kleur.dim(tr('new.success.step.run'))}`);
423
439
  console.log(` ${kleur.cyan('kasy run')}`);
424
- console.log(` ${kleur.dim('(ou F5 no VS Code)')}`);
440
+ console.log(` ${kleur.dim(tr('new.success.step.run.vscode'))}`);
425
441
  if (consoleUrl) {
426
442
  console.log('');
427
- console.log(` ${kleur.dim('3.')} ${kleur.dim(tr('new.success.step.console'))}`);
443
+ console.log(` ${kleur.dim(`${stepNum}.`)} ${kleur.dim(tr('new.success.step.console'))}`);
428
444
  console.log(` ${kleur.cyan(consoleUrl)}`);
429
445
  }
430
446
  console.log('');
@@ -842,6 +858,7 @@ async function runNew(directory, { language: langHint = null, backend: backendHi
842
858
  console.log(kleur.bold().yellow(`\n ${tr('new.firebase.create.beforeContinue.title')}`));
843
859
  console.log(kleur.gray(` ${tr(step1Key)}`));
844
860
  console.log(kleur.cyan(` ${authUrl}`));
861
+ openUrl(authUrl);
845
862
  const { ready } = await prompts(
846
863
  {
847
864
  type: 'confirm',
@@ -918,6 +935,7 @@ async function runNew(directory, { language: langHint = null, backend: backendHi
918
935
  console.log(kleur.bold().yellow(`\n ${tr('new.firebase.create.beforeContinue.title')}`));
919
936
  console.log(kleur.gray(` ${tr(step1Key)}`));
920
937
  console.log(kleur.cyan(` ${authUrl}`));
938
+ openUrl(authUrl);
921
939
  const { ready } = await prompts(
922
940
  {
923
941
  type: 'confirm',
@@ -1738,7 +1756,7 @@ async function runNew(directory, { language: langHint = null, backend: backendHi
1738
1756
  // ── FCM Service Account key (best effort via gcloud — Firebase uses ADC, Supabase needs JSON) ──
1739
1757
  let fcmServiceAccountJson = null;
1740
1758
  if (answers.firebaseProjectId) {
1741
- const fcmSpinner = ora(kleur.cyan('Gerando chave FCM (Service Account)…')).start();
1759
+ const fcmSpinner = ora(kleur.cyan(tr('new.fcm.generating'))).start();
1742
1760
  const fcmResult = await createFcmServiceAccountKey(answers.firebaseProjectId);
1743
1761
  if (fcmResult.ok) {
1744
1762
  fcmServiceAccountJson = fcmResult.json;
@@ -1787,7 +1805,7 @@ async function runNew(directory, { language: langHint = null, backend: backendHi
1787
1805
 
1788
1806
  // ── API: FCM Service Account key — save to .kasy/ for server configuration ──
1789
1807
  if (backend === 'api' && answers.firebaseProjectId) {
1790
- const fcmSpinner = ora(kleur.cyan('Gerando chave FCM (Service Account)…')).start();
1808
+ const fcmSpinner = ora(kleur.cyan(tr('new.fcm.generating'))).start();
1791
1809
  const fcmResult = await createFcmServiceAccountKey(answers.firebaseProjectId);
1792
1810
  if (fcmResult.ok) {
1793
1811
  fcmSpinner.stop();
@@ -1805,7 +1823,7 @@ async function runNew(directory, { language: langHint = null, backend: backendHi
1805
1823
  }
1806
1824
  }
1807
1825
  printStepResult({ name: 'fcm-key-saved', ok: true }, language);
1808
- console.log(kleur.gray(` Configure no servidor: FIREBASE_SERVICE_ACCOUNT_JSON="$(cat .kasy/fcm-service-account.json)"`));
1826
+ console.log(kleur.gray(` ${tr('new.fcm.serverConfig')}`));
1809
1827
  } catch (_) {
1810
1828
  printStepResult({ name: 'fcm-key-saved', ok: false, detail: 'salvar arquivo de chave falhou' }, language);
1811
1829
  }
@@ -1823,7 +1841,7 @@ async function runNew(directory, { language: langHint = null, backend: backendHi
1823
1841
  if (answers.firebaseProjectId && firebaseSetupMode === 'existing') {
1824
1842
  const flutterfireOkForSha1 = result.steps.find((s) => s.name === 'flutterfire')?.ok;
1825
1843
  if (flutterfireOkForSha1) {
1826
- const sha1Spinner = ora(kleur.cyan('Registrando SHA-1 (Google Sign-In Android)…')).start();
1844
+ const sha1Spinner = ora(kleur.cyan(tr('new.sha1.registering'))).start();
1827
1845
  const sha1Result = await registerDebugSha1(answers.firebaseProjectId, answers.bundleId);
1828
1846
  sha1Spinner.stop();
1829
1847
  if (sha1Result.ok) {
@@ -1832,9 +1850,11 @@ async function runNew(directory, { language: langHint = null, backend: backendHi
1832
1850
  }
1833
1851
  // existed === true → already registered, silent success
1834
1852
  } else {
1835
- console.log(kleur.yellow(` ⚠ SHA-1 não adicionado automaticamente: ${(sha1Result.sha1Error || '').slice(0, 120)}`));
1836
- console.log(kleur.yellow(` Adicione manualmente para o Google Sign-In funcionar no Android:`));
1837
- console.log(kleur.cyan(` https://console.firebase.google.com/project/${answers.firebaseProjectId}/settings/general/android:${answers.bundleId}`));
1853
+ const sha1ManualUrl = `https://console.firebase.google.com/project/${answers.firebaseProjectId}/settings/general/android:${answers.bundleId}`;
1854
+ console.log(kleur.yellow(` ${tr('new.sha1.failed', { error: (sha1Result.sha1Error || '').slice(0, 120) })}`));
1855
+ console.log(kleur.yellow(` ${tr('new.sha1.manual')}`));
1856
+ console.log(kleur.cyan(` ${sha1ManualUrl}`));
1857
+ openUrl(sha1ManualUrl);
1838
1858
  }
1839
1859
  }
1840
1860
  }
@@ -1856,11 +1876,13 @@ async function runNew(directory, { language: langHint = null, backend: backendHi
1856
1876
  // ── APNs reminder (all backends — required for iOS push notifications) ───────
1857
1877
  // Cannot be automated: the .p8 key only exists in Apple Developer Portal.
1858
1878
  if (answers.firebaseProjectId) {
1879
+ const apnsUrl = `https://console.firebase.google.com/project/${answers.firebaseProjectId}/settings/cloudmessaging`;
1859
1880
  console.log('');
1860
- console.log(kleur.bold().yellow(` ⚠ Push iOS: configure a APNs Key no Firebase Console`));
1861
- console.log(kleur.gray(` 1. Apple Developer Portal → Keys → criar APNs Key (.p8)`));
1862
- console.log(kleur.gray(` 2. Firebase Console → Cloud Messaging → app iOS → fazer upload da APNs Key`));
1863
- console.log(kleur.cyan(` https://console.firebase.google.com/project/${answers.firebaseProjectId}/settings/cloudmessaging`));
1881
+ console.log(kleur.bold().yellow(` ${tr('new.apns.warning')}`));
1882
+ console.log(kleur.gray(` ${tr('new.apns.step1')}`));
1883
+ console.log(kleur.gray(` ${tr('new.apns.step2')}`));
1884
+ console.log(kleur.cyan(` ${apnsUrl}`));
1885
+ openUrl(apnsUrl);
1864
1886
  }
1865
1887
 
1866
1888
  printSuccessCard(tr, answers, targetDir);
@@ -46,14 +46,18 @@ function isNewer(a, b) {
46
46
  * Collect changelog entries newer than `fromVersion` that affect the given modules.
47
47
  * Returns { module: ['v1.2.0: description', ...] }
48
48
  */
49
- function getChangesSince(changelog, fromVersion, modules) {
49
+ function getChangesSince(changelog, fromVersion, modules, language) {
50
+ const lang = (language || 'en').replace(/-.*/, '').toLowerCase();
50
51
  const changes = {};
51
52
  for (const [version, entry] of Object.entries(changelog)) {
52
53
  if (!isNewer(version, fromVersion || '0.0.0')) continue;
53
54
  for (const [mod, description] of Object.entries(entry.modules || {})) {
54
55
  if (modules.includes(mod)) {
56
+ const text = typeof description === 'object'
57
+ ? (description[lang] || description.en || description.pt || Object.values(description)[0])
58
+ : description;
55
59
  if (!changes[mod]) changes[mod] = [];
56
- changes[mod].push({ version, description });
60
+ changes[mod].push({ version, description: text });
57
61
  }
58
62
  }
59
63
  }
@@ -331,7 +335,7 @@ async function runUpdate(module, options = {}) {
331
335
 
332
336
  // ── Mode B: show status ──────────────────────────────────────────────────────
333
337
  const alreadyUpToDate = projectVersion && !isNewer(currentVersion, projectVersion);
334
- const changes = getChangesSince(changelog, projectVersion, activeModules);
338
+ const changes = getChangesSince(changelog, projectVersion, activeModules, options.language);
335
339
 
336
340
  // Modules in this project that have patch dirs (can be re-applied)
337
341
  const patchableModules = [];
@@ -1,6 +1,11 @@
1
1
  {
2
- "1.0.0": {
3
- "notes": "Initial release",
4
- "features": {}
2
+ "1.4.0": {
3
+ "modules": {
4
+ "components": {
5
+ "pt": "Melhorias no WebDevicePreview: minimizar/maximizar, orientação, locale e screenshot",
6
+ "en": "WebDevicePreview improvements: minimize/maximize, orientation, locale and screenshot",
7
+ "es": "Mejoras en WebDevicePreview: minimizar/maximizar, orientación, locale y captura de pantalla"
8
+ }
9
+ }
5
10
  }
6
11
  }
package/lib/utils/i18n.js CHANGED
@@ -551,8 +551,29 @@ const MESSAGES = {
551
551
  'new.success.title': 'Project created successfully!',
552
552
  'new.success.nextSteps': 'Next steps:',
553
553
  'new.success.step.cd': 'Go to your project folder:',
554
+ 'new.success.step.deploy': 'Deploy your backend to Firebase:',
554
555
  'new.success.step.run': 'Run your app (with your configured keys):',
556
+ 'new.success.step.run.vscode': '(or F5 in VS Code)',
555
557
  'new.success.step.console': 'Open your backend console:',
558
+ 'new.fcm.generating': 'Generating FCM Service Account key…',
559
+ 'new.sha1.registering': 'Registering SHA-1 for Google Sign-In (Android)…',
560
+ 'new.sha1.failed': 'SHA-1 not added automatically: {error}',
561
+ 'new.sha1.manual': 'Add it manually so Google Sign-In works on Android:',
562
+ 'new.sha1.skipped.apiFailed': 'SHA-1 not added automatically. Reason: {error}',
563
+ 'new.sha1.skipped.other': 'SHA-1 not added: {reason}',
564
+ 'new.sha1.addManually': 'Add manually: Firebase Console → Project settings → Your apps → Android → Add fingerprint',
565
+ 'new.sha1.added': '✓ SHA-1 added (Google Sign-In)',
566
+ 'new.firestore.created': '✓ Firestore created automatically',
567
+ 'new.firestore.notCreated.error': '⚠ Firestore not created. Reason: {error}',
568
+ 'new.firestore.notCreated': '⚠ Firestore not created automatically',
569
+ 'new.storage.created': '✓ Firebase Storage created automatically',
570
+ 'new.storage.notCreated.error': '⚠ Storage not created. Reason: {error}',
571
+ 'new.storage.notCreated': '⚠ Storage not created automatically',
572
+ 'new.activateManually': 'Activate manually in the console:',
573
+ 'new.fcm.serverConfig': 'Configure on your server: FIREBASE_SERVICE_ACCOUNT_JSON="$(cat .kasy/fcm-service-account.json)"',
574
+ 'new.apns.warning': '⚠ iOS Push: configure the APNs Key in Firebase Console',
575
+ 'new.apns.step1': '1. Apple Developer Portal → Keys → create APNs Key (.p8)',
576
+ 'new.apns.step2': '2. Firebase Console → Cloud Messaging → iOS app → upload APNs Key',
556
577
  'new.firebase.create.estimatedTime': '(usually 3–5 min — do not close the terminal)',
557
578
  'new.internet.warning': '📶 Make sure you have a stable internet connection — this step requires network access.',
558
579
 
@@ -1240,8 +1261,29 @@ const MESSAGES = {
1240
1261
  'new.success.title': 'Projeto criado com sucesso!',
1241
1262
  'new.success.nextSteps': 'Proximos passos:',
1242
1263
  'new.success.step.cd': 'Entre na pasta do projeto:',
1264
+ 'new.success.step.deploy': 'Publique o backend no Firebase:',
1243
1265
  'new.success.step.run': 'Execute o app (com suas chaves configuradas):',
1266
+ 'new.success.step.run.vscode': '(ou F5 no VS Code)',
1244
1267
  'new.success.step.console': 'Abra o console do backend:',
1268
+ 'new.fcm.generating': 'Gerando chave de Service Account FCM…',
1269
+ 'new.sha1.registering': 'Registrando SHA-1 para Google Sign-In (Android)…',
1270
+ 'new.sha1.failed': 'SHA-1 nao adicionado automaticamente: {error}',
1271
+ 'new.sha1.manual': 'Adicione manualmente para o Google Sign-In funcionar no Android:',
1272
+ 'new.sha1.skipped.apiFailed': 'SHA-1 nao adicionado automaticamente. Motivo: {error}',
1273
+ 'new.sha1.skipped.other': 'SHA-1 nao adicionado: {reason}',
1274
+ 'new.sha1.addManually': 'Adicione manualmente: Firebase Console → Configuracoes do projeto → Seus apps → Android → Adicionar impressao digital',
1275
+ 'new.sha1.added': '✓ SHA-1 adicionado (Google Sign-In)',
1276
+ 'new.firestore.created': '✓ Firestore criado automaticamente',
1277
+ 'new.firestore.notCreated.error': '⚠ Firestore nao criado. Motivo: {error}',
1278
+ 'new.firestore.notCreated': '⚠ Firestore nao criado automaticamente',
1279
+ 'new.storage.created': '✓ Firebase Storage criado automaticamente',
1280
+ 'new.storage.notCreated.error': '⚠ Storage nao criado. Motivo: {error}',
1281
+ 'new.storage.notCreated': '⚠ Storage nao criado automaticamente',
1282
+ 'new.activateManually': 'Ative manualmente no console:',
1283
+ 'new.fcm.serverConfig': 'Configure no servidor: FIREBASE_SERVICE_ACCOUNT_JSON="$(cat .kasy/fcm-service-account.json)"',
1284
+ 'new.apns.warning': '⚠ Push iOS: configure a APNs Key no Firebase Console',
1285
+ 'new.apns.step1': '1. Apple Developer Portal → Keys → criar APNs Key (.p8)',
1286
+ 'new.apns.step2': '2. Firebase Console → Cloud Messaging → app iOS → fazer upload da APNs Key',
1245
1287
  'new.firebase.create.estimatedTime': '(geralmente 3-5 min — nao feche o terminal)',
1246
1288
  'new.internet.warning': '📶 Verifique se voce esta com uma internet estavel — esta etapa precisa de conexao.',
1247
1289
 
@@ -1929,8 +1971,29 @@ const MESSAGES = {
1929
1971
  'new.success.title': '¡Proyecto creado con exito!',
1930
1972
  'new.success.nextSteps': 'Proximos pasos:',
1931
1973
  'new.success.step.cd': 'Ve a la carpeta del proyecto:',
1974
+ 'new.success.step.deploy': 'Despliega el backend en Firebase:',
1932
1975
  'new.success.step.run': 'Ejecuta el app (con tus claves configuradas):',
1976
+ 'new.success.step.run.vscode': '(o F5 en VS Code)',
1933
1977
  'new.success.step.console': 'Abre la consola del backend:',
1978
+ 'new.fcm.generating': 'Generando clave de Service Account FCM…',
1979
+ 'new.sha1.registering': 'Registrando SHA-1 para Google Sign-In (Android)…',
1980
+ 'new.sha1.failed': 'SHA-1 no añadido automaticamente: {error}',
1981
+ 'new.sha1.manual': 'Agregalo manualmente para que Google Sign-In funcione en Android:',
1982
+ 'new.sha1.skipped.apiFailed': 'SHA-1 no añadido automáticamente. Motivo: {error}',
1983
+ 'new.sha1.skipped.other': 'SHA-1 no añadido: {reason}',
1984
+ 'new.sha1.addManually': 'Agrega manualmente: Firebase Console → Configuración del proyecto → Tus apps → Android → Agregar huella digital',
1985
+ 'new.sha1.added': '✓ SHA-1 añadido (Google Sign-In)',
1986
+ 'new.firestore.created': '✓ Firestore creado automáticamente',
1987
+ 'new.firestore.notCreated.error': '⚠ Firestore no creado. Motivo: {error}',
1988
+ 'new.firestore.notCreated': '⚠ Firestore no creado automáticamente',
1989
+ 'new.storage.created': '✓ Firebase Storage creado automáticamente',
1990
+ 'new.storage.notCreated.error': '⚠ Storage no creado. Motivo: {error}',
1991
+ 'new.storage.notCreated': '⚠ Storage no creado automáticamente',
1992
+ 'new.activateManually': 'Activa manualmente en la consola:',
1993
+ 'new.fcm.serverConfig': 'Configura en tu servidor: FIREBASE_SERVICE_ACCOUNT_JSON="$(cat .kasy/fcm-service-account.json)"',
1994
+ 'new.apns.warning': '⚠ Push iOS: configura la APNs Key en Firebase Console',
1995
+ 'new.apns.step1': '1. Apple Developer Portal → Keys → crear APNs Key (.p8)',
1996
+ 'new.apns.step2': '2. Firebase Console → Cloud Messaging → app iOS → subir la APNs Key',
1934
1997
  'new.firebase.create.estimatedTime': '(generalmente 3-5 min — no cierres la terminal)',
1935
1998
  'new.internet.warning': '📶 Asegurate de tener una conexion a internet estable — este paso requiere red.',
1936
1999
 
@@ -117,11 +117,12 @@ async function ensureLicenseKey(options = {}) {
117
117
 
118
118
  // Key not found or deactivated
119
119
  if (!result.valid) {
120
- setStoredLicenseKey('');
121
- setCachedValidation({ valid: false });
122
120
  if (result.reason === 'inactive') {
121
+ // Admin deactivated this key (fraud/chargeback) — clear it so user can enter a new one
122
+ setStoredLicenseKey('');
123
123
  throw new Error(t('license.inactive'));
124
124
  }
125
+ // Not found: keep the stored key (might not be in DB yet), never cache failures
125
126
  throw new Error(t('license.invalid'));
126
127
  }
127
128
 
@@ -73,7 +73,7 @@ async function checkForUpdates() {
73
73
  kleur.bold(`v${cache.latestVersion} disponível`) +
74
74
  kleur.dim(` (você tem v${pkg.version})`) +
75
75
  '\n' +
76
- kleur.dim(' ') + kleur.cyan('npm install -g kasy-cli') +
76
+ kleur.dim(' ') + kleur.cyan('kasy upgrade') +
77
77
  '\n'
78
78
  );
79
79
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "kasy-cli",
3
- "version": "1.3.1",
3
+ "version": "1.4.0",
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"