kasy-cli 1.21.2 → 1.21.3
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 +18 -1
- package/lib/commands/doctor.js +33 -13
- package/lib/utils/checks.js +35 -0
- package/lib/utils/i18n/messages-en.js +5 -0
- package/lib/utils/i18n/messages-es.js +5 -0
- package/lib/utils/i18n/messages-pt.js +5 -0
- package/package.json +1 -1
package/bin/kasy.js
CHANGED
|
@@ -436,9 +436,26 @@ function buildProgram(language) {
|
|
|
436
436
|
.description(t('cli.command.upgrade.description'))
|
|
437
437
|
.action(() => {
|
|
438
438
|
const { spawnSync } = require('node:child_process');
|
|
439
|
+
const path = require('node:path');
|
|
439
440
|
printCompactHeader(t);
|
|
440
441
|
console.log(kleur.cyan(t('cli.command.upgrade.running')) + '\n');
|
|
441
|
-
|
|
442
|
+
// Update INTO the prefix the CLI actually lives in. The installer uses
|
|
443
|
+
// `npm install -g --prefix ~/.kasy`, so a bare `npm install -g` would
|
|
444
|
+
// land in npm's default global prefix and the old copy on PATH would
|
|
445
|
+
// keep winning. Derive the prefix from this file's own location:
|
|
446
|
+
// unix: <prefix>/lib/node_modules/kasy-cli/bin → drop lib + node_modules
|
|
447
|
+
// win: <prefix>/node_modules/kasy-cli/bin → drop node_modules
|
|
448
|
+
const segs = __dirname.split(path.sep);
|
|
449
|
+
const nm = segs.lastIndexOf('node_modules');
|
|
450
|
+
let prefix = null;
|
|
451
|
+
if (nm > 0) {
|
|
452
|
+
let p = segs.slice(0, nm);
|
|
453
|
+
if (p[p.length - 1] === 'lib') p = p.slice(0, -1);
|
|
454
|
+
prefix = p.join(path.sep) || null;
|
|
455
|
+
}
|
|
456
|
+
const args = ['install', '-g', 'kasy-cli@latest'];
|
|
457
|
+
if (prefix) args.push('--prefix', prefix);
|
|
458
|
+
const result = spawnSync('npm', args, { stdio: 'inherit', shell: true });
|
|
442
459
|
if (result.status === 0) {
|
|
443
460
|
console.log('\n' + kleur.green('✓ ' + t('cli.command.upgrade.done')) + '\n');
|
|
444
461
|
} else {
|
package/lib/commands/doctor.js
CHANGED
|
@@ -6,9 +6,10 @@ const { createTranslator, detectDefaultLanguage } = require('../utils/i18n');
|
|
|
6
6
|
const {
|
|
7
7
|
getBaseChecks,
|
|
8
8
|
getPlatformChecks,
|
|
9
|
-
getBackendChecks,
|
|
10
9
|
runChecks,
|
|
11
|
-
hasRequiredFailures
|
|
10
|
+
hasRequiredFailures,
|
|
11
|
+
probeTools,
|
|
12
|
+
BACKEND_TOOLS,
|
|
12
13
|
} = require('../utils/checks');
|
|
13
14
|
const {
|
|
14
15
|
validateAppleSetup,
|
|
@@ -21,11 +22,35 @@ const { validateGoogleIosUrlScheme, validateAppleSignInEntitlement, validateFace
|
|
|
21
22
|
const { listBillingAccounts, checkGcloudAuth } = require('../scaffold/backends/firebase/setup-from-scratch');
|
|
22
23
|
const { printCompactHeader } = require('../utils/brand');
|
|
23
24
|
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
25
|
+
/**
|
|
26
|
+
* Read-only backend tools status + a per-backend "what you need" view, so
|
|
27
|
+
* `kasy doctor` answers "what do I have, what's missing, and for which backend".
|
|
28
|
+
*/
|
|
29
|
+
async function printBackendReadiness(t) {
|
|
30
|
+
const status = await probeTools();
|
|
31
|
+
|
|
32
|
+
ui.log.step(kleur.bold(t('doctor.tools.title')));
|
|
33
|
+
for (const key of ['firebase', 'flutterfire', 'supabase', 'gcloud']) {
|
|
34
|
+
const s = status[key];
|
|
35
|
+
if (s.ok) {
|
|
36
|
+
ui.log.success(`${s.name}${s.version ? ` — ${s.version}` : ''}`);
|
|
37
|
+
} else if (key === 'gcloud') {
|
|
38
|
+
ui.log.message(kleur.dim(`○ ${s.name} — ${t('doctor.gcloud.note')}`));
|
|
39
|
+
} else {
|
|
40
|
+
ui.log.warn(`${s.name} — ${t('checks.notFound.short')}`);
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
ui.log.step(kleur.bold(t('doctor.byBackend.title')));
|
|
45
|
+
const labels = { firebase: '🔥 Firebase', supabase: '🟢 Supabase', api: '🔗 API REST' };
|
|
46
|
+
for (const [backend, req] of Object.entries(BACKEND_TOOLS)) {
|
|
47
|
+
const missing = req.required.filter((k) => !status[k].ok).map((k) => status[k].name);
|
|
48
|
+
if (missing.length === 0) {
|
|
49
|
+
ui.log.success(`${labels[backend]} — ${t('doctor.backend.ready')}`);
|
|
50
|
+
} else {
|
|
51
|
+
ui.log.warn(`${labels[backend]} — ${t('doctor.backend.missing', { tools: missing.join(', ') })}`);
|
|
52
|
+
}
|
|
53
|
+
}
|
|
29
54
|
}
|
|
30
55
|
|
|
31
56
|
/**
|
|
@@ -242,12 +267,7 @@ async function runDoctor(options = {}) {
|
|
|
242
267
|
{ t, compact: true, spinnerLabel: t('doctor.baseEnvironment'), doneLabel: t('doctor.baseEnvironment') }
|
|
243
268
|
);
|
|
244
269
|
|
|
245
|
-
|
|
246
|
-
if (optionalBackend.length > 0) {
|
|
247
|
-
await runChecks(optionalBackend, t('doctor.optionalBackend'), {
|
|
248
|
-
t, compact: true, spinnerLabel: t('doctor.optionalBackend'), doneLabel: t('doctor.optionalBackend'),
|
|
249
|
-
});
|
|
250
|
-
}
|
|
270
|
+
await printBackendReadiness(t);
|
|
251
271
|
|
|
252
272
|
// ── Google Cloud billing account (Firebase Blaze) ─────────────────────
|
|
253
273
|
// Only meaningful when gcloud is authenticated; otherwise users already saw
|
package/lib/utils/checks.js
CHANGED
|
@@ -406,6 +406,39 @@ function hasRequiredFailures(results) {
|
|
|
406
406
|
return results.some((result) => result.required && !result.ok);
|
|
407
407
|
}
|
|
408
408
|
|
|
409
|
+
// Backend tools probed by `kasy doctor`, plus which backend needs which. gcloud
|
|
410
|
+
// is listed only as Firebase-optional (create-from-scratch), matching the rule
|
|
411
|
+
// that Supabase/API never need it. Keep in sync with BACKEND_CHECKS above.
|
|
412
|
+
const PROBE_TOOLS = [
|
|
413
|
+
{ key: 'firebase', name: 'Firebase CLI', command: 'firebase --version' },
|
|
414
|
+
{ key: 'flutterfire', name: 'FlutterFire CLI', command: 'flutterfire --version', pubGlobalBin: 'flutterfire' },
|
|
415
|
+
{ key: 'supabase', name: 'Supabase CLI', command: 'supabase --version' },
|
|
416
|
+
{ key: 'gcloud', name: 'gcloud', command: 'gcloud --version' },
|
|
417
|
+
];
|
|
418
|
+
|
|
419
|
+
const BACKEND_TOOLS = {
|
|
420
|
+
firebase: { required: ['firebase', 'flutterfire'], optional: ['gcloud'] },
|
|
421
|
+
supabase: { required: ['firebase', 'flutterfire', 'supabase'], optional: [] },
|
|
422
|
+
api: { required: ['firebase', 'flutterfire'], optional: [] },
|
|
423
|
+
};
|
|
424
|
+
|
|
425
|
+
/**
|
|
426
|
+
* Read-only probe of the backend tools (never installs anything) — for the
|
|
427
|
+
* doctor's "what do I have / what's missing" view. Runs in parallel.
|
|
428
|
+
* @returns {Promise<Object<string, {name, ok, version}>>}
|
|
429
|
+
*/
|
|
430
|
+
async function probeTools(tools = PROBE_TOOLS) {
|
|
431
|
+
const entries = await Promise.all(tools.map(async (tool) => {
|
|
432
|
+
const res = await verifyTool(tool);
|
|
433
|
+
return [tool.key, {
|
|
434
|
+
name: tool.name,
|
|
435
|
+
ok: res.ok,
|
|
436
|
+
version: res.ok ? extractVersion(res.stdout, tool.name) : null,
|
|
437
|
+
}];
|
|
438
|
+
}));
|
|
439
|
+
return Object.fromEntries(entries);
|
|
440
|
+
}
|
|
441
|
+
|
|
409
442
|
module.exports = {
|
|
410
443
|
getBaseChecks,
|
|
411
444
|
getPlatformChecks,
|
|
@@ -413,4 +446,6 @@ module.exports = {
|
|
|
413
446
|
getInstallGuide,
|
|
414
447
|
runChecks,
|
|
415
448
|
hasRequiredFailures,
|
|
449
|
+
probeTools,
|
|
450
|
+
BACKEND_TOOLS,
|
|
416
451
|
};
|
|
@@ -80,6 +80,11 @@ module.exports = {
|
|
|
80
80
|
'doctor.title': 'Kasy Doctor',
|
|
81
81
|
'doctor.baseEnvironment': 'Base environment',
|
|
82
82
|
'doctor.optionalBackend': 'Optional backend tooling',
|
|
83
|
+
'doctor.tools.title': 'Backend tools',
|
|
84
|
+
'doctor.byBackend.title': 'What each backend needs',
|
|
85
|
+
'doctor.backend.ready': 'all set',
|
|
86
|
+
'doctor.backend.missing': 'still missing: {tools}',
|
|
87
|
+
'doctor.gcloud.note': 'optional, only to create a Firebase project from scratch',
|
|
83
88
|
'doctor.gcpBilling.title': 'Google Cloud Billing (Firebase Blaze)',
|
|
84
89
|
'doctor.gcpBilling.found': '{count} active billing account(s):',
|
|
85
90
|
'doctor.gcpBilling.missing': 'No billing account found. Create one before running `kasy new` with Firebase:',
|
|
@@ -80,6 +80,11 @@ module.exports = {
|
|
|
80
80
|
'doctor.title': 'Kasy Doctor',
|
|
81
81
|
'doctor.baseEnvironment': 'Entorno base',
|
|
82
82
|
'doctor.optionalBackend': 'Herramientas opcionales de backend',
|
|
83
|
+
'doctor.tools.title': 'Herramientas de backend',
|
|
84
|
+
'doctor.byBackend.title': 'Qué necesita cada backend',
|
|
85
|
+
'doctor.backend.ready': 'todo listo',
|
|
86
|
+
'doctor.backend.missing': 'falta instalar: {tools}',
|
|
87
|
+
'doctor.gcloud.note': 'opcional, solo para crear un proyecto Firebase desde cero',
|
|
83
88
|
'doctor.gcpBilling.title': 'Google Cloud Billing (Firebase Blaze)',
|
|
84
89
|
'doctor.gcpBilling.found': '{count} cuenta(s) de facturación activa(s):',
|
|
85
90
|
'doctor.gcpBilling.missing': 'No se encontró ninguna cuenta de facturación. Crea una antes de ejecutar `kasy new` con Firebase:',
|
|
@@ -80,6 +80,11 @@ module.exports = {
|
|
|
80
80
|
'doctor.title': 'Kasy Doctor',
|
|
81
81
|
'doctor.baseEnvironment': 'Ambiente base',
|
|
82
82
|
'doctor.optionalBackend': 'Ferramentas opcionais de backend',
|
|
83
|
+
'doctor.tools.title': 'Ferramentas de backend',
|
|
84
|
+
'doctor.byBackend.title': 'O que cada backend precisa',
|
|
85
|
+
'doctor.backend.ready': 'tudo pronto',
|
|
86
|
+
'doctor.backend.missing': 'falta instalar: {tools}',
|
|
87
|
+
'doctor.gcloud.note': 'opcional, só para criar um projeto Firebase do zero',
|
|
83
88
|
'doctor.gcpBilling.title': 'Google Cloud Billing (Firebase Blaze)',
|
|
84
89
|
'doctor.gcpBilling.found': '{count} conta(s) de faturamento ativa(s):',
|
|
85
90
|
'doctor.gcpBilling.missing': 'Nenhuma conta de faturamento encontrada. Crie uma antes de rodar `kasy new` com Firebase:',
|