smoonb 0.0.38 → 0.0.42

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/smoonb.js CHANGED
@@ -1,4 +1,4 @@
1
- #!/usr/bin/env node
1
+ #!/usr/bin/env node
2
2
 
3
3
  /**
4
4
  * smoonb - Complete Supabase backup and migration tool
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "smoonb",
3
- "version": "0.0.38",
3
+ "version": "0.0.42",
4
4
  "description": "Complete Supabase backup and migration tool - EXPERIMENTAL VERSION - USE AT YOUR OWN RISK",
5
5
  "preferGlobal": false,
6
6
  "preventGlobalInstall": true,
@@ -414,25 +414,58 @@ async function backupEdgeFunctionsWithDocker(projectId, accessToken, backupDir)
414
414
  let successCount = 0;
415
415
  let errorCount = 0;
416
416
 
417
- // ✅ Baixar cada Edge Function DIRETAMENTE para o backup (sem tocar em ./supabase/functions)
417
+ // ✅ Baixar cada Edge Function via Supabase CLI
418
+ // Nota: O CLI ignora o cwd e sempre baixa para supabase/functions
418
419
  for (const func of functions) {
419
420
  try {
420
421
  console.log(chalk.gray(` - Baixando: ${func.name}...`));
421
422
 
422
- // Criar diretório da função DIRETAMENTE no backup
423
+ // Criar diretório da função NO BACKUP
423
424
  const functionTargetDir = path.join(functionsDir, func.name);
424
425
  await ensureDir(functionTargetDir);
425
426
 
426
- // Baixar Edge Function via Supabase CLI DIRETAMENTE para o backup
427
+ // Diretório temporário onde o supabase CLI irá baixar (supabase/functions)
428
+ const tempDownloadDir = path.join(process.cwd(), 'supabase', 'functions', func.name);
429
+
430
+ // Baixar Edge Function via Supabase CLI (sempre vai para supabase/functions)
427
431
  const { execSync } = require('child_process');
428
432
 
429
- // Download DIRETO para o diretório de destino (sem intermediários)
430
433
  execSync(`supabase functions download ${func.name}`, {
431
- cwd: functionTargetDir,
432
434
  timeout: 60000,
433
435
  stdio: 'pipe'
434
436
  });
435
437
 
438
+ // ✅ COPIAR arquivos de supabase/functions para o backup
439
+ try {
440
+ const stat = await fs.stat(tempDownloadDir);
441
+ if (stat.isDirectory()) {
442
+ const files = await fs.readdir(tempDownloadDir);
443
+ for (const file of files) {
444
+ const srcPath = path.join(tempDownloadDir, file);
445
+ const dstPath = path.join(functionTargetDir, file);
446
+
447
+ const fileStats = await fs.stat(srcPath);
448
+ if (fileStats.isDirectory()) {
449
+ // Copiar diretórios recursivamente
450
+ await fs.cp(srcPath, dstPath, { recursive: true });
451
+ } else {
452
+ // Copiar arquivos
453
+ await fs.copyFile(srcPath, dstPath);
454
+ }
455
+ }
456
+ }
457
+ } catch (copyError) {
458
+ // Arquivos não foram baixados, continuar
459
+ console.log(chalk.yellow(` ⚠️ Nenhum arquivo encontrado em ${tempDownloadDir}`));
460
+ }
461
+
462
+ // ✅ LIMPAR supabase/functions após copiar
463
+ try {
464
+ await fs.rm(tempDownloadDir, { recursive: true, force: true });
465
+ } catch (cleanError) {
466
+ // Ignorar erro de limpeza
467
+ }
468
+
436
469
  console.log(chalk.green(` ✅ ${func.name} baixada com sucesso`));
437
470
  successCount++;
438
471
 
@@ -3,6 +3,7 @@ const path = require('path');
3
3
  const fs = require('fs');
4
4
  const { readConfig, getSourceProject, getTargetProject } = require('../utils/config');
5
5
  const { showBetaBanner } = require('../utils/banner');
6
+ const inquirer = require('inquirer');
6
7
 
7
8
  module.exports = async (options) => {
8
9
  showBetaBanner();
@@ -28,6 +29,12 @@ module.exports = async (options) => {
28
29
  // 3. Perguntar quais componentes restaurar
29
30
  const components = await askRestoreComponents(selectedBackup.path);
30
31
 
32
+ // Validar que pelo menos um componente foi selecionado
33
+ if (!Object.values(components).some(Boolean)) {
34
+ console.error(chalk.red('\n❌ Nenhum componente selecionado para restauração!'));
35
+ process.exit(1);
36
+ }
37
+
31
38
  // 4. Mostrar resumo
32
39
  showRestoreSummary(selectedBackup, components, targetProject);
33
40
 
@@ -41,27 +48,29 @@ module.exports = async (options) => {
41
48
  // 6. Executar restauração
42
49
  console.log(chalk.blue('\n🚀 Iniciando restauração...'));
43
50
 
44
- // 6.1 Database
45
- await restoreDatabaseGz(
46
- path.join(selectedBackup.path, selectedBackup.backupFile),
47
- targetProject.targetDatabaseUrl
48
- );
51
+ // 6.1 Database (se selecionado)
52
+ if (components.database) {
53
+ await restoreDatabaseGz(
54
+ path.join(selectedBackup.path, selectedBackup.backupFile),
55
+ targetProject.targetDatabaseUrl
56
+ );
57
+ }
49
58
 
50
59
  // 6.2 Edge Functions (se selecionado)
51
60
  if (components.edgeFunctions) {
52
61
  await restoreEdgeFunctions(selectedBackup.path, targetProject);
53
62
  }
54
63
 
55
- // 6.3 Storage Buckets (se selecionado)
56
- if (components.storage) {
57
- await restoreStorageBuckets(selectedBackup.path, targetProject);
58
- }
59
-
60
- // 6.4 Auth Settings (se selecionado)
64
+ // 6.3 Auth Settings (se selecionado)
61
65
  if (components.authSettings) {
62
66
  await restoreAuthSettings(selectedBackup.path, targetProject);
63
67
  }
64
68
 
69
+ // 6.4 Storage Buckets (se selecionado)
70
+ if (components.storage) {
71
+ await restoreStorageBuckets(selectedBackup.path, targetProject);
72
+ }
73
+
65
74
  // 6.5 Database Settings (se selecionado)
66
75
  if (components.databaseSettings) {
67
76
  await restoreDatabaseSettings(selectedBackup.path, targetProject);
@@ -169,59 +178,80 @@ async function selectBackupInteractive(backups) {
169
178
 
170
179
  // Perguntar quais componentes restaurar
171
180
  async function askRestoreComponents(backupPath) {
172
- const components = {
173
- edgeFunctions: true,
174
- storage: false,
175
- authSettings: false,
176
- databaseSettings: false,
177
- realtimeSettings: false
178
- };
181
+ const questions = [];
179
182
 
180
- const readline = require('readline').createInterface({
181
- input: process.stdin,
182
- output: process.stdout
183
+ // Database
184
+ questions.push({
185
+ type: 'confirm',
186
+ name: 'restoreDatabase',
187
+ message: 'Deseja restaurar Database?',
188
+ default: true
183
189
  });
184
190
 
185
- const question = (query) => new Promise(resolve => readline.question(query, resolve));
186
-
187
- console.log(chalk.blue('\n📦 Selecione os componentes para restaurar:'));
188
-
189
191
  // Edge Functions
190
192
  const edgeFunctionsDir = path.join(backupPath, 'edge-functions');
191
193
  if (fs.existsSync(edgeFunctionsDir) && fs.readdirSync(edgeFunctionsDir).length > 0) {
192
- const edgeChoice = await question('Deseja restaurar Edge Functions? (S/n): ');
193
- components.edgeFunctions = edgeChoice.toLowerCase() !== 'n';
194
+ questions.push({
195
+ type: 'confirm',
196
+ name: 'restoreEdgeFunctions',
197
+ message: 'Deseja restaurar Edge Functions?',
198
+ default: true
199
+ });
200
+ }
201
+
202
+ // Auth Settings
203
+ if (fs.existsSync(path.join(backupPath, 'auth-settings.json'))) {
204
+ questions.push({
205
+ type: 'confirm',
206
+ name: 'restoreAuthSettings',
207
+ message: 'Deseja restaurar Auth Settings (interativo)?',
208
+ default: true
209
+ });
194
210
  }
195
211
 
196
212
  // Storage Buckets
197
213
  const storageDir = path.join(backupPath, 'storage');
198
214
  if (fs.existsSync(storageDir) && fs.readdirSync(storageDir).length > 0) {
199
- const storageChoice = await question('Deseja restaurar Storage Buckets? (s/N): ');
200
- components.storage = storageChoice.toLowerCase() === 's';
215
+ questions.push({
216
+ type: 'confirm',
217
+ name: 'restoreStorage',
218
+ message: 'Deseja ver informações de Storage Buckets?',
219
+ default: false
220
+ });
201
221
  }
202
222
 
203
- // Auth Settings
204
- if (fs.existsSync(path.join(backupPath, 'auth-settings.json'))) {
205
- const authChoice = await question('Deseja restaurar Auth Settings? (s/N): ');
206
- components.authSettings = authChoice.toLowerCase() === 's';
207
- }
208
-
209
- // Database Settings
223
+ // Database Extensions and Settings
210
224
  const dbSettingsFiles = fs.readdirSync(backupPath)
211
225
  .filter(file => file.startsWith('database-settings-') && file.endsWith('.json'));
212
226
  if (dbSettingsFiles.length > 0) {
213
- const dbChoice = await question('Deseja restaurar Database Extensions and Settings? (s/N): ');
214
- components.databaseSettings = dbChoice.toLowerCase() === 's';
227
+ questions.push({
228
+ type: 'confirm',
229
+ name: 'restoreDatabaseSettings',
230
+ message: 'Deseja restaurar Database Extensions and Settings?',
231
+ default: false
232
+ });
215
233
  }
216
234
 
217
235
  // Realtime Settings
218
236
  if (fs.existsSync(path.join(backupPath, 'realtime-settings.json'))) {
219
- const realtimeChoice = await question('Deseja restaurar Realtime Settings? (s/N): ');
220
- components.realtimeSettings = realtimeChoice.toLowerCase() === 's';
237
+ questions.push({
238
+ type: 'confirm',
239
+ name: 'restoreRealtimeSettings',
240
+ message: 'Deseja restaurar Realtime Settings (interativo)?',
241
+ default: true
242
+ });
221
243
  }
222
244
 
223
- readline.close();
224
- return components;
245
+ const answers = await inquirer.prompt(questions);
246
+
247
+ return {
248
+ database: answers.restoreDatabase,
249
+ edgeFunctions: answers.restoreEdgeFunctions || false,
250
+ storage: answers.restoreStorage || false,
251
+ authSettings: answers.restoreAuthSettings || false,
252
+ databaseSettings: answers.restoreDatabaseSettings || false,
253
+ realtimeSettings: answers.restoreRealtimeSettings || false
254
+ };
225
255
  }
226
256
 
227
257
  // Mostrar resumo da restauração
@@ -235,7 +265,9 @@ function showRestoreSummary(backup, components, targetProject) {
235
265
  console.log(chalk.cyan('Componentes que serão restaurados:'));
236
266
  console.log('');
237
267
 
238
- console.log('✅ Database (psql -f via Docker)');
268
+ if (components.database) {
269
+ console.log('✅ Database (psql -f via Docker)');
270
+ }
239
271
 
240
272
  if (components.edgeFunctions) {
241
273
  const edgeFunctionsDir = path.join(backup.path, 'edge-functions');
@@ -246,20 +278,20 @@ function showRestoreSummary(backup, components, targetProject) {
246
278
  functions.forEach(func => console.log(` - ${func}`));
247
279
  }
248
280
 
249
- if (components.storage) {
250
- console.log('📦 Storage Buckets: Restaurar buckets e objetos');
281
+ if (components.authSettings) {
282
+ console.log('🔐 Auth Settings: Exibir URL e valores para configuração manual');
251
283
  }
252
284
 
253
- if (components.authSettings) {
254
- console.log('🔐 Auth Settings: Restaurar configurações de autenticação');
285
+ if (components.storage) {
286
+ console.log('📦 Storage Buckets: Exibir informações e instruções do Google Colab');
255
287
  }
256
288
 
257
289
  if (components.databaseSettings) {
258
- console.log('🔧 Database Settings: Restaurar extensões e configurações');
290
+ console.log('🔧 Database Extensions and Settings: Restaurar via SQL');
259
291
  }
260
292
 
261
293
  if (components.realtimeSettings) {
262
- console.log('🔄 Realtime Settings: Restaurar configurações do Realtime');
294
+ console.log('🔄 Realtime Settings: Exibir URL e valores para configuração manual');
263
295
  }
264
296
 
265
297
  console.log('');
@@ -354,37 +386,251 @@ async function restoreDatabaseGz(backupFilePath, targetDatabaseUrl) {
354
386
  }
355
387
  }
356
388
 
357
- // Restaurar Edge Functions (placeholder - implementar via Management API)
389
+ // Restaurar Edge Functions via supabase functions deploy
358
390
  async function restoreEdgeFunctions(backupPath, targetProject) {
359
- console.log(chalk.blue('⚡ Restaurando Edge Functions...'));
360
- console.log(chalk.yellow(' ℹ️ Deploy de Edge Functions via Management API ainda não implementado'));
361
- // TODO: Implementar deploy via Supabase Management API
391
+ console.log(chalk.blue('\n⚡ Restaurando Edge Functions...'));
392
+
393
+ try {
394
+ const { execSync } = require('child_process');
395
+ const edgeFunctionsDir = path.join(backupPath, 'edge-functions');
396
+
397
+ if (!fs.existsSync(edgeFunctionsDir)) {
398
+ console.log(chalk.yellow(' ⚠️ Nenhuma Edge Function encontrada no backup'));
399
+ return;
400
+ }
401
+
402
+ const functions = fs.readdirSync(edgeFunctionsDir).filter(item =>
403
+ fs.statSync(path.join(edgeFunctionsDir, item)).isDirectory()
404
+ );
405
+
406
+ if (functions.length === 0) {
407
+ console.log(chalk.yellow(' ⚠️ Nenhuma Edge Function encontrada no backup'));
408
+ return;
409
+ }
410
+
411
+ console.log(chalk.gray(` - Encontradas ${functions.length} Edge Function(s)`));
412
+
413
+ // Link com projeto target
414
+ console.log(chalk.gray(` - Linkando com projeto ${targetProject.targetProjectId}...`));
415
+
416
+ try {
417
+ execSync(`supabase link --project-ref ${targetProject.targetProjectId}`, {
418
+ stdio: 'pipe',
419
+ encoding: 'utf8'
420
+ });
421
+ } catch (linkError) {
422
+ console.log(chalk.yellow(` ⚠️ Link pode já existir, continuando...`));
423
+ }
424
+
425
+ // Deploy de cada função
426
+ for (const funcName of functions) {
427
+ console.log(chalk.gray(` - Deployando ${funcName}...`));
428
+
429
+ try {
430
+ const functionPath = path.join(edgeFunctionsDir, funcName);
431
+
432
+ execSync(`supabase functions deploy ${funcName}`, {
433
+ cwd: functionPath,
434
+ stdio: 'pipe',
435
+ encoding: 'utf8'
436
+ });
437
+
438
+ console.log(chalk.green(` ✅ ${funcName} deployada com sucesso!`));
439
+ } catch (deployError) {
440
+ console.log(chalk.yellow(` ⚠️ ${funcName} - deploy falhou: ${deployError.message}`));
441
+ }
442
+ }
443
+
444
+ } catch (error) {
445
+ console.error(chalk.red(` ❌ Erro ao restaurar Edge Functions: ${error.message}`));
446
+ }
362
447
  }
363
448
 
364
- // Restaurar Storage Buckets (placeholder)
449
+ // Restaurar Storage Buckets (interativo - exibir informações)
365
450
  async function restoreStorageBuckets(backupPath, targetProject) {
366
- console.log(chalk.blue('📦 Restaurando Storage Buckets...'));
367
- console.log(chalk.yellow(' ℹ️ Restauração de Storage Buckets ainda não implementado'));
368
- // TODO: Implementar restauração via Management API
451
+ console.log(chalk.blue('\n📦 Restaurando Storage Buckets...'));
452
+
453
+ try {
454
+ const storageDir = path.join(backupPath, 'storage');
455
+
456
+ if (!fs.existsSync(storageDir)) {
457
+ console.log(chalk.yellow(' ⚠️ Nenhum bucket de Storage encontrado no backup'));
458
+ return;
459
+ }
460
+
461
+ const manifestPath = path.join(backupPath, 'backup-manifest.json');
462
+ let manifest = null;
463
+
464
+ if (fs.existsSync(manifestPath)) {
465
+ manifest = JSON.parse(fs.readFileSync(manifestPath, 'utf8'));
466
+ }
467
+
468
+ const buckets = manifest?.components?.storage?.buckets || [];
469
+
470
+ if (buckets.length === 0) {
471
+ console.log(chalk.gray(' ℹ️ Nenhum bucket para restaurar'));
472
+ return;
473
+ }
474
+
475
+ console.log(chalk.green(`\n ✅ ${buckets.length} bucket(s) encontrado(s) no backup`));
476
+ buckets.forEach(bucket => {
477
+ console.log(chalk.gray(` - ${bucket.name} (${bucket.public ? 'público' : 'privado'})`));
478
+ });
479
+
480
+ const colabUrl = 'https://colab.research.google.com/github/PLyn/supabase-storage-migrate/blob/main/Supabase_Storage_migration.ipynb';
481
+
482
+ console.log(chalk.yellow('\n ⚠️ Migração de objetos de Storage requer processo manual'));
483
+ console.log(chalk.cyan(` ℹ️ Use o script do Google Colab: ${colabUrl}`));
484
+ console.log(chalk.gray('\n 📋 Instruções:'));
485
+ console.log(chalk.gray(' 1. Execute o script no Google Colab'));
486
+ console.log(chalk.gray(' 2. Configure as credenciais dos projetos (origem e destino)'));
487
+ console.log(chalk.gray(' 3. Execute a migração'));
488
+
489
+ await inquirer.prompt([{
490
+ type: 'input',
491
+ name: 'continue',
492
+ message: 'Pressione Enter para continuar'
493
+ }]);
494
+
495
+ } catch (error) {
496
+ console.error(chalk.red(` ❌ Erro ao processar Storage: ${error.message}`));
497
+ }
369
498
  }
370
499
 
371
- // Restaurar Auth Settings (placeholder)
500
+ // Restaurar Auth Settings (interativo - exibir URL e valores)
372
501
  async function restoreAuthSettings(backupPath, targetProject) {
373
- console.log(chalk.blue('🔐 Restaurando Auth Settings...'));
374
- console.log(chalk.yellow(' ℹ️ Restauração de Auth Settings ainda não implementado'));
375
- // TODO: Implementar via Management API
502
+ console.log(chalk.blue('\n🔐 Restaurando Auth Settings...'));
503
+
504
+ try {
505
+ const authSettingsPath = path.join(backupPath, 'auth-settings.json');
506
+
507
+ if (!fs.existsSync(authSettingsPath)) {
508
+ console.log(chalk.yellow(' ⚠️ Nenhuma configuração de Auth encontrada no backup'));
509
+ return;
510
+ }
511
+
512
+ const authSettings = JSON.parse(fs.readFileSync(authSettingsPath, 'utf8'));
513
+ const dashboardUrl = `https://supabase.com/dashboard/project/${targetProject.targetProjectId}/auth/url-config`;
514
+
515
+ console.log(chalk.green('\n ✅ URL para configuração manual:'));
516
+ console.log(chalk.cyan(` ${dashboardUrl}`));
517
+ console.log(chalk.yellow('\n 📋 Configure manualmente as seguintes opções:'));
518
+
519
+ if (authSettings.auth_url_config) {
520
+ Object.entries(authSettings.auth_url_config).forEach(([key, value]) => {
521
+ console.log(chalk.gray(` - ${key}: ${value}`));
522
+ });
523
+ }
524
+
525
+ console.log(chalk.yellow('\n ⚠️ Após configurar, pressione Enter para continuar...'));
526
+
527
+ await inquirer.prompt([{
528
+ type: 'input',
529
+ name: 'continue',
530
+ message: 'Pressione Enter para continuar'
531
+ }]);
532
+
533
+ console.log(chalk.green(' ✅ Auth Settings processados'));
534
+
535
+ } catch (error) {
536
+ console.error(chalk.red(` ❌ Erro ao processar Auth Settings: ${error.message}`));
537
+ }
376
538
  }
377
539
 
378
- // Restaurar Database Settings (placeholder)
540
+ // Restaurar Database Settings (via SQL)
379
541
  async function restoreDatabaseSettings(backupPath, targetProject) {
380
- console.log(chalk.blue('🔧 Restaurando Database Settings...'));
381
- console.log(chalk.yellow(' ℹ️ Restauração de Database Settings ainda não implementado'));
382
- // TODO: Aplicar extensões e configurações via SQL
542
+ console.log(chalk.blue('\n🔧 Restaurando Database Settings...'));
543
+
544
+ try {
545
+ const files = fs.readdirSync(backupPath);
546
+ const dbSettingsFile = files.find(f => f.startsWith('database-settings-') && f.endsWith('.json'));
547
+
548
+ if (!dbSettingsFile) {
549
+ console.log(chalk.yellow(' ⚠️ Nenhuma configuração de Database encontrada no backup'));
550
+ return;
551
+ }
552
+
553
+ const dbSettings = JSON.parse(fs.readFileSync(path.join(backupPath, dbSettingsFile), 'utf8'));
554
+ const { execSync } = require('child_process');
555
+
556
+ if (dbSettings.extensions && dbSettings.extensions.length > 0) {
557
+ console.log(chalk.gray(` - Habilitando ${dbSettings.extensions.length} extension(s)...`));
558
+
559
+ for (const ext of dbSettings.extensions) {
560
+ console.log(chalk.gray(` - ${ext}`));
561
+
562
+ const sqlCommand = `CREATE EXTENSION IF NOT EXISTS ${ext};`;
563
+
564
+ const urlMatch = targetProject.targetDatabaseUrl.match(/postgresql:\/\/([^@:]+):([^@]+)@(.+)$/);
565
+
566
+ if (!urlMatch) {
567
+ console.log(chalk.yellow(` ⚠️ URL inválida para ${ext}`));
568
+ continue;
569
+ }
570
+
571
+ const dockerCmd = [
572
+ 'docker run --rm',
573
+ '--network host',
574
+ `-e PGPASSWORD="${encodeURIComponent(urlMatch[2])}"`,
575
+ 'postgres:17 psql',
576
+ `-d "${targetProject.targetDatabaseUrl}"`,
577
+ `-c "${sqlCommand}"`
578
+ ].join(' ');
579
+
580
+ try {
581
+ execSync(dockerCmd, { stdio: 'pipe', encoding: 'utf8' });
582
+ } catch (sqlError) {
583
+ console.log(chalk.yellow(` ⚠️ ${ext} - extension já existe ou não pode ser habilitada`));
584
+ }
585
+ }
586
+ }
587
+
588
+ console.log(chalk.green(' ✅ Database Settings restaurados com sucesso!'));
589
+
590
+ } catch (error) {
591
+ console.error(chalk.red(` ❌ Erro ao restaurar Database Settings: ${error.message}`));
592
+ }
383
593
  }
384
594
 
385
- // Restaurar Realtime Settings (placeholder)
595
+ // Restaurar Realtime Settings (interativo - exibir URL e valores)
386
596
  async function restoreRealtimeSettings(backupPath, targetProject) {
387
- console.log(chalk.blue('🔄 Restaurando Realtime Settings...'));
388
- console.log(chalk.yellow(' ℹ️ Realtime Settings requerem configuração manual no Dashboard'));
389
- // TODO: Adicionar instruções de configuração manual
597
+ console.log(chalk.blue('\n🔄 Restaurando Realtime Settings...'));
598
+
599
+ try {
600
+ const realtimeSettingsPath = path.join(backupPath, 'realtime-settings.json');
601
+
602
+ if (!fs.existsSync(realtimeSettingsPath)) {
603
+ console.log(chalk.yellow(' ⚠️ Nenhuma configuração de Realtime encontrada no backup'));
604
+ return;
605
+ }
606
+
607
+ const realtimeSettings = JSON.parse(fs.readFileSync(realtimeSettingsPath, 'utf8'));
608
+ const dashboardUrl = `https://supabase.com/dashboard/project/${targetProject.targetProjectId}/realtime/settings`;
609
+
610
+ console.log(chalk.green('\n ✅ URL para configuração manual:'));
611
+ console.log(chalk.cyan(` ${dashboardUrl}`));
612
+ console.log(chalk.yellow('\n 📋 Configure manualmente as seguintes opções:'));
613
+
614
+ if (realtimeSettings.realtime_settings?.settings) {
615
+ Object.entries(realtimeSettings.realtime_settings.settings).forEach(([key, setting]) => {
616
+ console.log(chalk.gray(` - ${setting.label}: ${setting.value}`));
617
+ if (setting.description) {
618
+ console.log(chalk.gray(` ${setting.description}`));
619
+ }
620
+ });
621
+ }
622
+
623
+ console.log(chalk.yellow('\n ⚠️ Após configurar, pressione Enter para continuar...'));
624
+
625
+ await inquirer.prompt([{
626
+ type: 'input',
627
+ name: 'continue',
628
+ message: 'Pressione Enter para continuar'
629
+ }]);
630
+
631
+ console.log(chalk.green(' ✅ Realtime Settings processados'));
632
+
633
+ } catch (error) {
634
+ console.error(chalk.red(` ❌ Erro ao processar Realtime Settings: ${error.message}`));
635
+ }
390
636
  }