kasy-cli 1.5.3 → 1.6.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.
- package/bin/kasy.js +5 -2
- package/lib/commands/add.js +101 -100
- package/lib/commands/check.js +55 -38
- package/lib/commands/codemagic.js +61 -58
- package/lib/commands/deploy.js +49 -45
- package/lib/commands/docs.js +19 -18
- package/lib/commands/doctor.js +46 -44
- package/lib/commands/features.js +20 -17
- package/lib/commands/ios.js +69 -69
- package/lib/commands/new.js +529 -771
- package/lib/commands/notifications.js +59 -59
- package/lib/commands/remove.js +28 -27
- package/lib/commands/run.js +3 -1
- package/lib/commands/update.js +104 -96
- package/lib/commands/validate.js +24 -19
- package/lib/utils/apple-release.js +23 -11
- package/lib/utils/brand.js +72 -0
- package/lib/utils/checks.js +20 -9
- package/lib/utils/prompts.js +82 -142
- package/lib/utils/ui.js +92 -0
- package/lib/utils/updates.js +9 -8
- package/package.json +2 -1
package/lib/utils/checks.js
CHANGED
|
@@ -3,6 +3,7 @@ const { promisify } = require('node:util');
|
|
|
3
3
|
const readline = require('node:readline');
|
|
4
4
|
const kleur = require('kleur');
|
|
5
5
|
const oraPackage = require('ora');
|
|
6
|
+
const ui = require('./ui');
|
|
6
7
|
const { createTranslator, detectDefaultLanguage } = require('./i18n');
|
|
7
8
|
|
|
8
9
|
const execAsync = promisify(exec);
|
|
@@ -290,7 +291,8 @@ async function runChecks(checks, title, options = {}) {
|
|
|
290
291
|
|
|
291
292
|
// Compact mode: single spinner, show failures only
|
|
292
293
|
const { spinnerLabel = title, doneLabel = title } = options;
|
|
293
|
-
|
|
294
|
+
const spinner = ui.spinner();
|
|
295
|
+
spinner.start(spinnerLabel);
|
|
294
296
|
|
|
295
297
|
const results = [];
|
|
296
298
|
for (const check of checks) {
|
|
@@ -298,26 +300,35 @@ async function runChecks(checks, title, options = {}) {
|
|
|
298
300
|
}
|
|
299
301
|
|
|
300
302
|
const failures = results.filter((r) => !r.ok);
|
|
303
|
+
const requiredFailures = failures.filter((r) => r.required);
|
|
301
304
|
|
|
302
305
|
if (failures.length === 0) {
|
|
303
|
-
|
|
306
|
+
spinner.stop(doneLabel);
|
|
304
307
|
return results;
|
|
305
308
|
}
|
|
306
309
|
|
|
310
|
+
// Close the spinner reflecting what actually happened: red ▲ if a required
|
|
311
|
+
// check failed, default green ✦ if only optional checks failed (warnings).
|
|
312
|
+
if (requiredFailures.length > 0) {
|
|
313
|
+
spinner.error(doneLabel);
|
|
314
|
+
} else {
|
|
315
|
+
spinner.stop(doneLabel);
|
|
316
|
+
}
|
|
317
|
+
|
|
307
318
|
for (const result of failures) {
|
|
308
|
-
const hint = result.failHint ? `\n
|
|
319
|
+
const hint = result.failHint ? `\n${kleur.dim(`→ ${result.failHint}`)}` : '';
|
|
309
320
|
if (result.required) {
|
|
310
321
|
const detail = result.autoInstallFailed ? ` — ${t('checks.install.failed')}` : '';
|
|
311
|
-
const diagSuffix = result.diagnosis ? `\n
|
|
312
|
-
|
|
322
|
+
const diagSuffix = result.diagnosis ? `\n${kleur.dim(`→ ${t(`checks.diagnostic.${result.diagnosis}`, { name: result.name })}`)}` : '';
|
|
323
|
+
ui.log.error(`${t('checks.missing', { name: result.name })}${detail}${diagSuffix}${hint}`);
|
|
313
324
|
} else if (result.diagnosis) {
|
|
314
|
-
|
|
325
|
+
ui.log.warn(t(`checks.diagnostic.${result.diagnosis}`, { name: result.name }));
|
|
315
326
|
} else if (result.warnMessage) {
|
|
316
|
-
|
|
327
|
+
ui.log.warn(`${result.warnMessage}${hint}`);
|
|
317
328
|
} else if (result.warnMessageKey) {
|
|
318
|
-
|
|
329
|
+
ui.log.warn(`${t(result.warnMessageKey)}${hint}`);
|
|
319
330
|
} else {
|
|
320
|
-
|
|
331
|
+
ui.log.warn(`${t('checks.notFound', { name: result.name })}${hint}`);
|
|
321
332
|
}
|
|
322
333
|
}
|
|
323
334
|
|
package/lib/utils/prompts.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
const
|
|
1
|
+
const ui = require('./ui');
|
|
2
2
|
const { isLicenseFormatValid } = require('./license');
|
|
3
3
|
const {
|
|
4
4
|
LANGUAGE_CHOICES,
|
|
@@ -15,11 +15,9 @@ const {
|
|
|
15
15
|
|
|
16
16
|
const KASY_AUDIENCE = process.env.KASY_INTERNAL === '1' ? 'internal' : 'public';
|
|
17
17
|
|
|
18
|
-
function
|
|
19
|
-
return {
|
|
20
|
-
|
|
21
|
-
throw new Error(t('prompt.cancelled'));
|
|
22
|
-
}
|
|
18
|
+
function makeCancel(t) {
|
|
19
|
+
return () => {
|
|
20
|
+
throw new Error(t('prompt.cancelled'));
|
|
23
21
|
};
|
|
24
22
|
}
|
|
25
23
|
|
|
@@ -30,178 +28,120 @@ async function promptLanguage(language) {
|
|
|
30
28
|
}
|
|
31
29
|
|
|
32
30
|
const detectedLanguage = detectDefaultLanguage();
|
|
33
|
-
const initial = Math.max(
|
|
34
|
-
0,
|
|
35
|
-
LANGUAGE_CHOICES.findIndex((choice) => choice.value === detectedLanguage)
|
|
36
|
-
);
|
|
37
|
-
|
|
38
31
|
const t = createTranslator(detectedLanguage);
|
|
39
|
-
const response = await prompts(
|
|
40
|
-
{
|
|
41
|
-
type: 'select',
|
|
42
|
-
name: 'language',
|
|
43
|
-
message: t('prompt.language.select'),
|
|
44
|
-
choices: LANGUAGE_CHOICES,
|
|
45
|
-
initial
|
|
46
|
-
},
|
|
47
|
-
getPromptOptions(t)
|
|
48
|
-
);
|
|
49
32
|
|
|
50
|
-
|
|
33
|
+
const value = await ui.select({
|
|
34
|
+
message: t('prompt.language.select'),
|
|
35
|
+
initialValue: detectedLanguage,
|
|
36
|
+
options: LANGUAGE_CHOICES.map((choice) => ({
|
|
37
|
+
value: choice.value,
|
|
38
|
+
label: choice.title,
|
|
39
|
+
})),
|
|
40
|
+
onCancel: makeCancel(t),
|
|
41
|
+
});
|
|
42
|
+
|
|
43
|
+
return normalizeLanguage(value) || detectedLanguage;
|
|
51
44
|
}
|
|
52
45
|
|
|
53
46
|
async function promptLicenseKey(initial = '', options = {}) {
|
|
54
47
|
const t = options.t || createTranslator(options.language);
|
|
55
|
-
const
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
if (isLicenseFormatValid(value)) {
|
|
63
|
-
return true;
|
|
64
|
-
}
|
|
65
|
-
|
|
66
|
-
return t('prompt.license.invalid');
|
|
67
|
-
}
|
|
68
|
-
},
|
|
69
|
-
getPromptOptions(t)
|
|
70
|
-
);
|
|
71
|
-
|
|
72
|
-
return response.licenseKey;
|
|
48
|
+
const value = await ui.text({
|
|
49
|
+
message: t('prompt.license.enter'),
|
|
50
|
+
initialValue: initial,
|
|
51
|
+
validate: (v) => (isLicenseFormatValid(v) ? undefined : t('prompt.license.invalid')),
|
|
52
|
+
onCancel: makeCancel(t),
|
|
53
|
+
});
|
|
54
|
+
return value;
|
|
73
55
|
}
|
|
74
56
|
|
|
75
57
|
async function promptProjectName(options = {}) {
|
|
76
58
|
const t = options.t || createTranslator(options.language);
|
|
77
|
-
const
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
},
|
|
85
|
-
getPromptOptions(t)
|
|
86
|
-
);
|
|
87
|
-
return response.projectName.trim();
|
|
59
|
+
const value = await ui.text({
|
|
60
|
+
message: t('prompt.projectName.enter'),
|
|
61
|
+
initialValue: t('prompt.projectName.default'),
|
|
62
|
+
validate: (v) => (v && v.trim() ? undefined : t('prompt.projectName.required')),
|
|
63
|
+
onCancel: makeCancel(t),
|
|
64
|
+
});
|
|
65
|
+
return String(value).trim();
|
|
88
66
|
}
|
|
89
67
|
|
|
90
68
|
async function runSetupWizard(options = {}) {
|
|
91
69
|
const t = options.t || createTranslator(options.language);
|
|
70
|
+
const cancel = makeCancel(t);
|
|
92
71
|
const defaultAppName = options.defaultAppName || 'MyApp';
|
|
93
72
|
const selectedFeaturesFromArgv = parseFeatureList(options.selectedFeatures);
|
|
94
73
|
|
|
95
|
-
const
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
74
|
+
const appName = await ui.text({
|
|
75
|
+
message: t('prompt.appName.enter'),
|
|
76
|
+
initialValue: defaultAppName,
|
|
77
|
+
validate: (v) => (v && v.trim() ? undefined : t('prompt.appName.required')),
|
|
78
|
+
onCancel: cancel,
|
|
79
|
+
});
|
|
80
|
+
const bundleId = await ui.text({
|
|
81
|
+
message: t('prompt.bundleId.enter'),
|
|
82
|
+
initialValue: 'com.example.app',
|
|
83
|
+
validate: (v) => {
|
|
84
|
+
if (!v || !v.trim()) return t('prompt.bundleId.required');
|
|
85
|
+
const valid = /^[a-zA-Z][\w]*(\.[a-zA-Z][\w]*)+$/.test(v.trim());
|
|
86
|
+
return valid ? undefined : t('prompt.bundleId.invalid');
|
|
102
87
|
},
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
initial: 'com.example.app',
|
|
108
|
-
validate: (value) => {
|
|
109
|
-
if (!value || !value.trim()) {
|
|
110
|
-
return t('prompt.bundleId.required');
|
|
111
|
-
}
|
|
112
|
-
|
|
113
|
-
const valid = /^[a-zA-Z][\w]*(\.[a-zA-Z][\w]*)+$/.test(value.trim());
|
|
114
|
-
return valid ? true : t('prompt.bundleId.invalid');
|
|
115
|
-
}
|
|
116
|
-
}
|
|
117
|
-
];
|
|
88
|
+
onCancel: cancel,
|
|
89
|
+
});
|
|
90
|
+
|
|
91
|
+
const core = { appName, bundleId };
|
|
118
92
|
|
|
119
93
|
const backendFromArgv = normalizeBackend(options.selectedBackend);
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
name: 'backend',
|
|
94
|
+
let backend = backendFromArgv;
|
|
95
|
+
if (!backend) {
|
|
96
|
+
backend = await ui.select({
|
|
124
97
|
message: t('prompt.backend.select'),
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
})),
|
|
129
|
-
initial: 0
|
|
98
|
+
initialValue: AVAILABLE_BACKENDS[0]?.id,
|
|
99
|
+
options: AVAILABLE_BACKENDS.map((b) => ({ value: b.id, label: b.id })),
|
|
100
|
+
onCancel: cancel,
|
|
130
101
|
});
|
|
131
102
|
}
|
|
132
103
|
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
104
|
+
let features;
|
|
105
|
+
if (selectedFeaturesFromArgv.length > 0) {
|
|
106
|
+
features = selectedFeaturesFromArgv;
|
|
107
|
+
} else {
|
|
108
|
+
const selected = await ui.multiselect({
|
|
137
109
|
message: t('prompt.features.select'),
|
|
138
|
-
|
|
139
|
-
choices: getVisibleFeatures({ audience: KASY_AUDIENCE }).map((feature) => ({
|
|
140
|
-
title: feature.status === 'internal' ? `${feature.id} [beta]` : feature.id,
|
|
110
|
+
options: getVisibleFeatures({ audience: KASY_AUDIENCE }).map((feature) => ({
|
|
141
111
|
value: feature.id,
|
|
142
|
-
|
|
112
|
+
label: feature.status === 'internal' ? `${feature.id} [beta]` : feature.id,
|
|
143
113
|
})),
|
|
144
|
-
|
|
114
|
+
initialValues: [],
|
|
115
|
+
required: false,
|
|
116
|
+
onCancel: cancel,
|
|
145
117
|
});
|
|
118
|
+
features = parseFeatureList(selected);
|
|
146
119
|
}
|
|
147
120
|
|
|
148
|
-
const core = await prompts(baseQuestions, getPromptOptions(t));
|
|
149
|
-
const backend = backendFromArgv || core.backend;
|
|
150
|
-
const features = selectedFeaturesFromArgv.length > 0
|
|
151
|
-
? selectedFeaturesFromArgv
|
|
152
|
-
: parseFeatureList(core.features);
|
|
153
|
-
|
|
154
121
|
if (backend === 'firebase') {
|
|
155
|
-
const
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
},
|
|
162
|
-
getPromptOptions(t)
|
|
163
|
-
);
|
|
164
|
-
|
|
165
|
-
return {
|
|
166
|
-
...core,
|
|
167
|
-
backend,
|
|
168
|
-
features,
|
|
169
|
-
...firebaseAnswers
|
|
170
|
-
};
|
|
122
|
+
const firebaseProjectId = await ui.text({
|
|
123
|
+
message: t('prompt.firebase.projectId.enter'),
|
|
124
|
+
validate: (v) => (v && v.trim() ? undefined : t('prompt.firebase.projectId.required')),
|
|
125
|
+
onCancel: cancel,
|
|
126
|
+
});
|
|
127
|
+
return { ...core, backend, features, firebaseProjectId };
|
|
171
128
|
}
|
|
172
129
|
|
|
173
130
|
if (backend === 'supabase') {
|
|
174
|
-
const
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
message: t('prompt.supabase.anonKey.enter'),
|
|
186
|
-
validate: (value) => (value && value.trim() ? true : t('prompt.supabase.anonKey.required'))
|
|
187
|
-
}
|
|
188
|
-
],
|
|
189
|
-
getPromptOptions(t)
|
|
190
|
-
);
|
|
191
|
-
|
|
192
|
-
return {
|
|
193
|
-
...core,
|
|
194
|
-
backend,
|
|
195
|
-
features,
|
|
196
|
-
...supabaseAnswers
|
|
197
|
-
};
|
|
131
|
+
const supabaseUrl = await ui.text({
|
|
132
|
+
message: t('prompt.supabase.url.enter'),
|
|
133
|
+
validate: (v) => (v && v.trim() ? undefined : t('prompt.supabase.url.required')),
|
|
134
|
+
onCancel: cancel,
|
|
135
|
+
});
|
|
136
|
+
const supabaseAnonKey = await ui.text({
|
|
137
|
+
message: t('prompt.supabase.anonKey.enter'),
|
|
138
|
+
validate: (v) => (v && v.trim() ? undefined : t('prompt.supabase.anonKey.required')),
|
|
139
|
+
onCancel: cancel,
|
|
140
|
+
});
|
|
141
|
+
return { ...core, backend, features, supabaseUrl, supabaseAnonKey };
|
|
198
142
|
}
|
|
199
143
|
|
|
200
|
-
return {
|
|
201
|
-
...core,
|
|
202
|
-
backend,
|
|
203
|
-
features
|
|
204
|
-
};
|
|
144
|
+
return { ...core, backend, features };
|
|
205
145
|
}
|
|
206
146
|
|
|
207
147
|
module.exports = {
|
package/lib/utils/ui.js
CHANGED
|
@@ -48,6 +48,21 @@ async function text({ message, placeholder, initialValue, defaultValue, validate
|
|
|
48
48
|
return handleCancel(result, onCancel);
|
|
49
49
|
}
|
|
50
50
|
|
|
51
|
+
async function password({ message, mask, validate, onCancel }) {
|
|
52
|
+
const result = await clack.password({
|
|
53
|
+
message,
|
|
54
|
+
mask,
|
|
55
|
+
validate: validate
|
|
56
|
+
? (value) => {
|
|
57
|
+
const out = validate(value);
|
|
58
|
+
if (out === true || out == null) return undefined;
|
|
59
|
+
return out;
|
|
60
|
+
}
|
|
61
|
+
: undefined,
|
|
62
|
+
});
|
|
63
|
+
return handleCancel(result, onCancel);
|
|
64
|
+
}
|
|
65
|
+
|
|
51
66
|
async function confirm({ message, initialValue = true, onCancel }) {
|
|
52
67
|
const result = await clack.confirm({ message, initialValue });
|
|
53
68
|
return handleCancel(result, onCancel);
|
|
@@ -63,13 +78,87 @@ function outro(message) { clack.outro(message); }
|
|
|
63
78
|
function note(message, title) { clack.note(message, title); }
|
|
64
79
|
function cancel(message) { clack.cancel(message); }
|
|
65
80
|
|
|
81
|
+
/**
|
|
82
|
+
* Spinner integrated with Clack's vertical line (│).
|
|
83
|
+
* Returns: { start(msg), message(msg), stop(msg, code?) }
|
|
84
|
+
* code: 0 = success (✦), 1 = cancel (■), 2 = error (▲)
|
|
85
|
+
*/
|
|
66
86
|
function spinner() { return clack.spinner(); }
|
|
67
87
|
|
|
88
|
+
/**
|
|
89
|
+
* Multi-step spinner: each .next(text) succeeds the previous step
|
|
90
|
+
* with the previous message, then starts a new step with `text`.
|
|
91
|
+
* Mimics the old ora-based makeProgressSpinner() but uses Clack's
|
|
92
|
+
* vertical-line aesthetic so the line stays connected.
|
|
93
|
+
*/
|
|
94
|
+
function makeStepper() {
|
|
95
|
+
let current = null;
|
|
96
|
+
let currentMsg = '';
|
|
97
|
+
return {
|
|
98
|
+
next(text) {
|
|
99
|
+
if (current) current.stop(currentMsg);
|
|
100
|
+
current = clack.spinner();
|
|
101
|
+
currentMsg = text;
|
|
102
|
+
current.start(text);
|
|
103
|
+
},
|
|
104
|
+
update(text) {
|
|
105
|
+
if (current) {
|
|
106
|
+
currentMsg = text;
|
|
107
|
+
current.message(text);
|
|
108
|
+
}
|
|
109
|
+
},
|
|
110
|
+
succeed(text) {
|
|
111
|
+
if (current) {
|
|
112
|
+
current.stop(text || currentMsg);
|
|
113
|
+
current = null;
|
|
114
|
+
currentMsg = '';
|
|
115
|
+
}
|
|
116
|
+
},
|
|
117
|
+
fail(text) {
|
|
118
|
+
if (current) {
|
|
119
|
+
// .error() renders the red ▲ icon; .stop() defaults to green ✦ success.
|
|
120
|
+
current.error(text || currentMsg);
|
|
121
|
+
current = null;
|
|
122
|
+
currentMsg = '';
|
|
123
|
+
}
|
|
124
|
+
},
|
|
125
|
+
warn(text) {
|
|
126
|
+
// Clack has no "warn" terminal state — prefix with ⚠ and close as success.
|
|
127
|
+
if (current) {
|
|
128
|
+
current.stop(`⚠ ${text || currentMsg}`);
|
|
129
|
+
current = null;
|
|
130
|
+
currentMsg = '';
|
|
131
|
+
}
|
|
132
|
+
},
|
|
133
|
+
stop() {
|
|
134
|
+
if (current) {
|
|
135
|
+
current.stop(currentMsg);
|
|
136
|
+
current = null;
|
|
137
|
+
currentMsg = '';
|
|
138
|
+
}
|
|
139
|
+
},
|
|
140
|
+
};
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
/**
|
|
144
|
+
* Long-running task with rolling log output (e.g. pub get, build_runner).
|
|
145
|
+
* Shows last N lines while running; collapses to a single ✦ on success.
|
|
146
|
+
* Options: { title, limit, retainLog }
|
|
147
|
+
*/
|
|
148
|
+
function taskLog(options) { return clack.taskLog(options); }
|
|
149
|
+
|
|
150
|
+
/**
|
|
151
|
+
* Progress bar for operations with known total steps.
|
|
152
|
+
* Returns: { start(msg), advance(n, msg?), message(msg), stop(msg) }
|
|
153
|
+
*/
|
|
154
|
+
function progress(options) { return clack.progress(options); }
|
|
155
|
+
|
|
68
156
|
const log = clack.log;
|
|
69
157
|
|
|
70
158
|
module.exports = {
|
|
71
159
|
select,
|
|
72
160
|
text,
|
|
161
|
+
password,
|
|
73
162
|
confirm,
|
|
74
163
|
multiselect,
|
|
75
164
|
intro,
|
|
@@ -77,6 +166,9 @@ module.exports = {
|
|
|
77
166
|
note,
|
|
78
167
|
cancel,
|
|
79
168
|
spinner,
|
|
169
|
+
makeStepper,
|
|
170
|
+
taskLog,
|
|
171
|
+
progress,
|
|
80
172
|
log,
|
|
81
173
|
isCancel: clack.isCancel,
|
|
82
174
|
};
|
package/lib/utils/updates.js
CHANGED
|
@@ -4,8 +4,8 @@ const https = require('node:https');
|
|
|
4
4
|
const path = require('node:path');
|
|
5
5
|
const { existsSync, readJsonSync, writeJsonSync, ensureDirSync } = require('fs-extra');
|
|
6
6
|
const kleur = require('kleur');
|
|
7
|
-
const prompts = require('prompts');
|
|
8
7
|
const pkg = require('../../package.json');
|
|
8
|
+
const ui = require('./ui');
|
|
9
9
|
const { CONFIG_PATH } = require('./license');
|
|
10
10
|
|
|
11
11
|
const CHECK_INTERVAL_MS = 24 * 60 * 60 * 1000; // 24 horas
|
|
@@ -106,19 +106,20 @@ async function warnIfOutdatedBeforeNew(t) {
|
|
|
106
106
|
if (!cache.latestVersion || !isNewer(cache.latestVersion, pkg.version)) return;
|
|
107
107
|
|
|
108
108
|
const hint = t ? t('new.outdated.hint') : 'projetos criados agora não terão as últimas melhorias.';
|
|
109
|
-
console.log('');
|
|
110
109
|
console.log(
|
|
110
|
+
'\n' +
|
|
111
111
|
kleur.bgYellow().black(' UPDATE ') + ' ' +
|
|
112
112
|
kleur.bold(`v${cache.latestVersion} disponível`) +
|
|
113
|
-
kleur.dim(` — ${hint}`)
|
|
113
|
+
kleur.dim(` — ${hint}`) +
|
|
114
|
+
'\n'
|
|
114
115
|
);
|
|
115
|
-
console.log('');
|
|
116
116
|
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
117
|
+
// Called BEFORE any ui.intro fires in kasy new, so this prompt runs as a
|
|
118
|
+
// standalone Clack confirm (no rail to break). On cancel we exit cleanly.
|
|
119
|
+
const upgrade = await ui.confirm({
|
|
120
120
|
message: t ? t('new.outdated.upgradeNow') : 'Atualizar antes de criar? (requer assinatura ativa)',
|
|
121
|
-
|
|
121
|
+
initialValue: false,
|
|
122
|
+
onCancel: () => process.exit(0),
|
|
122
123
|
});
|
|
123
124
|
|
|
124
125
|
if (upgrade) {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "kasy-cli",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.6.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"
|
|
@@ -45,6 +45,7 @@
|
|
|
45
45
|
},
|
|
46
46
|
"dependencies": {
|
|
47
47
|
"@clack/prompts": "^1.4.0",
|
|
48
|
+
"boxen": "^8.0.1",
|
|
48
49
|
"commander": "^12.0.0",
|
|
49
50
|
"fs-extra": "^11.2.0",
|
|
50
51
|
"gradient-string": "^1.2.0",
|