gufi-cli 0.1.1 → 0.1.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.
@@ -0,0 +1,26 @@
1
+ /**
2
+ * gufi companies - List and manage companies
3
+ * gufi modules - List modules of a company
4
+ * gufi module - View/edit module JSON
5
+ */
6
+ export declare function companiesCommand(): Promise<void>;
7
+ export declare function modulesCommand(companyId?: string): Promise<void>;
8
+ export declare function moduleCommand(moduleId?: string, options?: {
9
+ edit?: boolean;
10
+ company?: string;
11
+ file?: string;
12
+ }): Promise<void>;
13
+ export declare function moduleUpdateCommand(moduleId?: string, jsonFile?: string, options?: {
14
+ company?: string;
15
+ dryRun?: boolean;
16
+ }): Promise<void>;
17
+ export declare function companyCreateCommand(name?: string): Promise<void>;
18
+ export declare function automationsCommand(_moduleId?: string, // Ignored - kept for backwards compatibility
19
+ options?: {
20
+ company?: string;
21
+ }): Promise<void>;
22
+ export declare function automationCommand(automationName?: string, options?: {
23
+ edit?: boolean;
24
+ company?: string;
25
+ file?: string;
26
+ }): Promise<void>;
@@ -0,0 +1,513 @@
1
+ /**
2
+ * gufi companies - List and manage companies
3
+ * gufi modules - List modules of a company
4
+ * gufi module - View/edit module JSON
5
+ */
6
+ import chalk from "chalk";
7
+ import { isLoggedIn, getApiUrl, getToken } from "../lib/config.js";
8
+ const API_URL_BASE = "https://gogufi.com";
9
+ async function apiRequest(path, options = {}) {
10
+ const token = getToken();
11
+ const apiUrl = getApiUrl() || API_URL_BASE;
12
+ const res = await fetch(`${apiUrl}${path}`, {
13
+ ...options,
14
+ headers: {
15
+ "Authorization": `Bearer ${token}`,
16
+ "Content-Type": "application/json",
17
+ ...options.headers,
18
+ },
19
+ });
20
+ if (!res.ok) {
21
+ const error = await res.text();
22
+ throw new Error(`API Error ${res.status}: ${error}`);
23
+ }
24
+ return res.json();
25
+ }
26
+ // ════════════════════════════════════════════════════════════════════
27
+ // gufi companies - List user's companies
28
+ // ════════════════════════════════════════════════════════════════════
29
+ export async function companiesCommand() {
30
+ if (!isLoggedIn()) {
31
+ console.log(chalk.red("\n ✗ No estás logueado. Usa: gufi login\n"));
32
+ process.exit(1);
33
+ }
34
+ console.log(chalk.magenta("\n 🟣 Gufi Companies\n"));
35
+ try {
36
+ const data = await apiRequest("/api/companies");
37
+ const companies = data.items || data.data || data || [];
38
+ if (!companies.length) {
39
+ console.log(chalk.gray(" No tienes companies asignadas.\n"));
40
+ return;
41
+ }
42
+ console.log(chalk.gray(" ID\tNombre\t\t\t\tCreado"));
43
+ console.log(chalk.gray(" ─".repeat(35)));
44
+ for (const c of companies) {
45
+ const id = String(c.id).padEnd(4);
46
+ const name = (c.name || "").substring(0, 24).padEnd(24);
47
+ const created = c.created_at ? new Date(c.created_at).toLocaleDateString() : "-";
48
+ console.log(` ${chalk.cyan(id)}\t${name}\t${created}`);
49
+ }
50
+ console.log(chalk.gray("\n 💡 Usa: gufi modules <company_id> para ver módulos\n"));
51
+ }
52
+ catch (err) {
53
+ console.log(chalk.red(`\n ✗ Error: ${err.message}\n`));
54
+ }
55
+ }
56
+ // ════════════════════════════════════════════════════════════════════
57
+ // gufi modules <company_id> - List modules of a company
58
+ // ════════════════════════════════════════════════════════════════════
59
+ export async function modulesCommand(companyId) {
60
+ if (!isLoggedIn()) {
61
+ console.log(chalk.red("\n ✗ No estás logueado. Usa: gufi login\n"));
62
+ process.exit(1);
63
+ }
64
+ if (!companyId) {
65
+ console.log(chalk.red("\n ✗ Uso: gufi modules <company_id>\n"));
66
+ console.log(chalk.gray(" Tip: Usa 'gufi companies' para ver tus companies\n"));
67
+ process.exit(1);
68
+ }
69
+ console.log(chalk.magenta(`\n 🟣 Módulos de Company ${companyId}\n`));
70
+ try {
71
+ // Get company schema which includes all modules
72
+ const data = await apiRequest("/api/company/schema", {
73
+ headers: { "X-Company-ID": companyId },
74
+ });
75
+ const modules = data.modules || data.data?.modules || [];
76
+ if (!modules.length) {
77
+ console.log(chalk.gray(" No hay módulos en esta company.\n"));
78
+ return;
79
+ }
80
+ console.log(chalk.gray(" ID\tNombre\t\t\t\tEntidades"));
81
+ console.log(chalk.gray(" ─".repeat(40)));
82
+ for (const mod of modules) {
83
+ // Count entities across all submodules and find moduleId
84
+ let entityCount = 0;
85
+ let moduleId;
86
+ for (const sub of mod.submodules || []) {
87
+ for (const ent of sub.entities || []) {
88
+ entityCount++;
89
+ if (!moduleId && ent.ui?.moduleId) {
90
+ moduleId = ent.ui.moduleId;
91
+ }
92
+ }
93
+ }
94
+ const id = String(moduleId || "?").padEnd(5);
95
+ const label = (mod.displayName || mod.label || mod.name || "").substring(0, 28).padEnd(28);
96
+ console.log(` ${chalk.cyan(id)}\t${label}\t${entityCount} entities`);
97
+ }
98
+ console.log(chalk.gray("\n 💡 Usa: gufi module <module_id> -c " + companyId + " para ver/editar\n"));
99
+ }
100
+ catch (err) {
101
+ console.log(chalk.red(`\n ✗ Error: ${err.message}\n`));
102
+ }
103
+ }
104
+ // ════════════════════════════════════════════════════════════════════
105
+ // Helper: Get module from company schema
106
+ // ════════════════════════════════════════════════════════════════════
107
+ async function getModuleFromSchema(moduleId, companyId) {
108
+ const headers = {};
109
+ if (companyId) {
110
+ headers["X-Company-ID"] = companyId;
111
+ }
112
+ const data = await apiRequest("/api/company/schema", { headers });
113
+ const modules = data.modules || data.data?.modules || [];
114
+ // Find module by ID (from entity ui.moduleId) or by name
115
+ const module = modules.find((m) => {
116
+ // Check if any entity has the matching moduleId
117
+ for (const sub of m.submodules || []) {
118
+ for (const ent of sub.entities || []) {
119
+ if (String(ent.ui?.moduleId) === String(moduleId)) {
120
+ return true;
121
+ }
122
+ }
123
+ }
124
+ // Also check by name
125
+ return m.name === moduleId;
126
+ });
127
+ if (!module) {
128
+ throw new Error(`Módulo ${moduleId} no encontrado en esta company`);
129
+ }
130
+ // Attach the moduleId to the module object for convenience
131
+ for (const sub of module.submodules || []) {
132
+ for (const ent of sub.entities || []) {
133
+ if (ent.ui?.moduleId) {
134
+ module._moduleId = ent.ui.moduleId;
135
+ break;
136
+ }
137
+ }
138
+ if (module._moduleId)
139
+ break;
140
+ }
141
+ return module;
142
+ }
143
+ // ════════════════════════════════════════════════════════════════════
144
+ // gufi module <module_id> [--edit] [--company <id>] - View/edit module JSON
145
+ // ════════════════════════════════════════════════════════════════════
146
+ export async function moduleCommand(moduleId, options) {
147
+ if (!isLoggedIn()) {
148
+ console.log(chalk.red("\n ✗ No estás logueado. Usa: gufi login\n"));
149
+ process.exit(1);
150
+ }
151
+ if (!moduleId) {
152
+ console.log(chalk.red("\n ✗ Uso: gufi module <module_id> [--edit] [--company <id>]\n"));
153
+ process.exit(1);
154
+ }
155
+ const companyId = options?.company;
156
+ console.log(chalk.magenta(`\n 🟣 Módulo ${moduleId}\n`));
157
+ try {
158
+ // Get module from company schema
159
+ const module = await getModuleFromSchema(moduleId, companyId);
160
+ const actualModuleId = module._moduleId || moduleId;
161
+ // Show module info
162
+ console.log(chalk.white(` ID: ${actualModuleId}`));
163
+ console.log(chalk.white(` Nombre: ${module.name || "N/A"}`));
164
+ console.log(chalk.white(` Label: ${module.displayName || module.label || module.name}`));
165
+ // Count entities
166
+ let entityCount = 0;
167
+ for (const sub of module.submodules || []) {
168
+ entityCount += (sub.entities || []).length;
169
+ }
170
+ console.log(chalk.white(` Entidades: ${entityCount}`));
171
+ console.log();
172
+ // Build a clean JSON definition for editing
173
+ const jsonDef = {
174
+ name: module.name,
175
+ displayName: module.displayName || module.label,
176
+ icon: module.icon,
177
+ submodules: module.submodules,
178
+ };
179
+ if (options?.edit) {
180
+ // Edit mode - open in editor
181
+ await editModuleJson(String(actualModuleId), jsonDef, companyId);
182
+ }
183
+ else if (options?.file) {
184
+ // Save to file
185
+ const fs = await import("fs");
186
+ fs.writeFileSync(options.file, JSON.stringify(jsonDef, null, 2));
187
+ console.log(chalk.green(` ✓ JSON guardado en: ${options.file}\n`));
188
+ }
189
+ else {
190
+ // Print JSON
191
+ console.log(chalk.gray(" JSON Definition:"));
192
+ console.log(chalk.gray(" ─".repeat(30)));
193
+ console.log(JSON.stringify(jsonDef, null, 2));
194
+ console.log(chalk.gray("\n 💡 Usa: gufi module <id> --edit para editar"));
195
+ console.log(chalk.gray(" 💡 Usa: gufi module <id> --file out.json para guardar\n"));
196
+ }
197
+ }
198
+ catch (err) {
199
+ console.log(chalk.red(`\n ✗ Error: ${err.message}\n`));
200
+ }
201
+ }
202
+ // ════════════════════════════════════════════════════════════════════
203
+ // Edit module JSON interactively
204
+ // ════════════════════════════════════════════════════════════════════
205
+ async function editModuleJson(moduleId, currentJson, companyId) {
206
+ const fs = await import("fs");
207
+ const os = await import("os");
208
+ const path = await import("path");
209
+ const { spawn } = await import("child_process");
210
+ // Write to temp file
211
+ const tempFile = path.join(os.tmpdir(), `gufi-module-${moduleId}.json`);
212
+ fs.writeFileSync(tempFile, JSON.stringify(currentJson, null, 2));
213
+ console.log(chalk.gray(` Abriendo editor... (${tempFile})`));
214
+ console.log(chalk.gray(" Guarda y cierra el editor para aplicar cambios.\n"));
215
+ // Determine editor
216
+ const editor = process.env.EDITOR || process.env.VISUAL || "nano";
217
+ // Open editor
218
+ const child = spawn(editor, [tempFile], {
219
+ stdio: "inherit",
220
+ shell: true,
221
+ });
222
+ await new Promise((resolve, reject) => {
223
+ child.on("close", (code) => {
224
+ if (code === 0)
225
+ resolve();
226
+ else
227
+ reject(new Error(`Editor exited with code ${code}`));
228
+ });
229
+ child.on("error", reject);
230
+ });
231
+ // Read modified file
232
+ const newContent = fs.readFileSync(tempFile, "utf-8");
233
+ let newJson;
234
+ try {
235
+ newJson = JSON.parse(newContent);
236
+ }
237
+ catch (e) {
238
+ console.log(chalk.red("\n ✗ JSON inválido. Cambios no guardados.\n"));
239
+ return;
240
+ }
241
+ // Check if changed
242
+ if (JSON.stringify(newJson) === JSON.stringify(currentJson)) {
243
+ console.log(chalk.gray(" Sin cambios.\n"));
244
+ fs.unlinkSync(tempFile);
245
+ return;
246
+ }
247
+ // Validate by calling updateModule (dry-run first)
248
+ console.log(chalk.yellow(" ⏳ Validando cambios..."));
249
+ try {
250
+ const headers = {};
251
+ if (companyId) {
252
+ headers["X-Company-ID"] = companyId;
253
+ }
254
+ // Call update endpoint
255
+ const result = await apiRequest(`/api/modules/${moduleId}`, {
256
+ method: "PUT",
257
+ headers,
258
+ body: JSON.stringify({ json_definition: newJson }),
259
+ });
260
+ console.log(chalk.green("\n ✓ Módulo actualizado correctamente!\n"));
261
+ // Show what changed
262
+ if (result.changes) {
263
+ console.log(chalk.gray(" Cambios aplicados:"));
264
+ for (const change of result.changes) {
265
+ console.log(chalk.gray(` - ${change}`));
266
+ }
267
+ }
268
+ }
269
+ catch (err) {
270
+ console.log(chalk.red(`\n ✗ Error al actualizar: ${err.message}`));
271
+ console.log(chalk.gray(` El archivo temporal se mantiene en: ${tempFile}\n`));
272
+ return;
273
+ }
274
+ // Cleanup temp file
275
+ fs.unlinkSync(tempFile);
276
+ }
277
+ // ════════════════════════════════════════════════════════════════════
278
+ // gufi module:update <module_id> <json_file> - Update module from file
279
+ // ════════════════════════════════════════════════════════════════════
280
+ export async function moduleUpdateCommand(moduleId, jsonFile, options) {
281
+ if (!isLoggedIn()) {
282
+ console.log(chalk.red("\n ✗ No estás logueado. Usa: gufi login\n"));
283
+ process.exit(1);
284
+ }
285
+ if (!moduleId || !jsonFile) {
286
+ console.log(chalk.red("\n ✗ Uso: gufi module:update <module_id> <json_file> [--company <id>] [--dry-run]\n"));
287
+ process.exit(1);
288
+ }
289
+ const fs = await import("fs");
290
+ // Read JSON file
291
+ let newJson;
292
+ try {
293
+ const content = fs.readFileSync(jsonFile, "utf-8");
294
+ newJson = JSON.parse(content);
295
+ }
296
+ catch (e) {
297
+ console.log(chalk.red(`\n ✗ Error leyendo archivo: ${e.message}\n`));
298
+ process.exit(1);
299
+ }
300
+ console.log(chalk.magenta(`\n 🟣 Actualizando módulo ${moduleId}\n`));
301
+ if (options?.dryRun) {
302
+ console.log(chalk.yellow(" [DRY RUN] Solo validando, no se guardarán cambios.\n"));
303
+ }
304
+ try {
305
+ const headers = {};
306
+ if (options?.company) {
307
+ headers["X-Company-ID"] = options.company;
308
+ }
309
+ // Validate first
310
+ console.log(chalk.gray(" Validando JSON..."));
311
+ const validateResult = await apiRequest(`/api/modules/${moduleId}/validate`, {
312
+ method: "POST",
313
+ headers,
314
+ body: JSON.stringify({ json_definition: newJson }),
315
+ });
316
+ if (validateResult.errors?.length) {
317
+ console.log(chalk.red("\n ✗ Errores de validación:"));
318
+ for (const err of validateResult.errors) {
319
+ console.log(chalk.red(` - ${err}`));
320
+ }
321
+ console.log();
322
+ process.exit(1);
323
+ }
324
+ console.log(chalk.green(" ✓ JSON válido"));
325
+ if (options?.dryRun) {
326
+ console.log(chalk.gray("\n [DRY RUN] Validación exitosa. Usa sin --dry-run para aplicar.\n"));
327
+ return;
328
+ }
329
+ // Apply changes
330
+ console.log(chalk.gray(" Aplicando cambios..."));
331
+ const result = await apiRequest(`/api/modules/${moduleId}`, {
332
+ method: "PUT",
333
+ headers,
334
+ body: JSON.stringify({ json_definition: newJson }),
335
+ });
336
+ console.log(chalk.green("\n ✓ Módulo actualizado correctamente!\n"));
337
+ if (result.tablesCreated?.length) {
338
+ console.log(chalk.gray(" Tablas creadas:"));
339
+ for (const t of result.tablesCreated) {
340
+ console.log(chalk.green(` + ${t}`));
341
+ }
342
+ }
343
+ if (result.columnsAdded?.length) {
344
+ console.log(chalk.gray(" Columnas añadidas:"));
345
+ for (const c of result.columnsAdded) {
346
+ console.log(chalk.green(` + ${c}`));
347
+ }
348
+ }
349
+ }
350
+ catch (err) {
351
+ console.log(chalk.red(`\n ✗ Error: ${err.message}\n`));
352
+ process.exit(1);
353
+ }
354
+ }
355
+ // ════════════════════════════════════════════════════════════════════
356
+ // gufi company:create <name> - Create a new company
357
+ // ════════════════════════════════════════════════════════════════════
358
+ export async function companyCreateCommand(name) {
359
+ if (!isLoggedIn()) {
360
+ console.log(chalk.red("\n ✗ No estás logueado. Usa: gufi login\n"));
361
+ process.exit(1);
362
+ }
363
+ if (!name) {
364
+ console.log(chalk.red("\n ✗ Uso: gufi company:create <nombre>\n"));
365
+ process.exit(1);
366
+ }
367
+ console.log(chalk.magenta(`\n 🟣 Creando company: ${name}\n`));
368
+ try {
369
+ const result = await apiRequest("/api/companies", {
370
+ method: "POST",
371
+ body: JSON.stringify({ name }),
372
+ });
373
+ const company = result.data || result;
374
+ console.log(chalk.green(" ✓ Company creada!\n"));
375
+ console.log(chalk.white(` ID: ${company.id}`));
376
+ console.log(chalk.white(` Nombre: ${company.name}`));
377
+ console.log(chalk.white(` Schema: company_${company.id}`));
378
+ console.log(chalk.gray("\n 💡 Usa: gufi modules " + company.id + " para ver módulos\n"));
379
+ }
380
+ catch (err) {
381
+ console.log(chalk.red(`\n ✗ Error: ${err.message}\n`));
382
+ }
383
+ }
384
+ // ════════════════════════════════════════════════════════════════════
385
+ // gufi automations - List all automation scripts of a company
386
+ // ════════════════════════════════════════════════════════════════════
387
+ export async function automationsCommand(_moduleId, // Ignored - kept for backwards compatibility
388
+ options) {
389
+ if (!isLoggedIn()) {
390
+ console.log(chalk.red("\n ✗ No estás logueado. Usa: gufi login\n"));
391
+ process.exit(1);
392
+ }
393
+ console.log(chalk.magenta("\n 🟣 Automation Scripts\n"));
394
+ try {
395
+ const headers = {};
396
+ if (options?.company) {
397
+ headers["X-Company-ID"] = options.company;
398
+ }
399
+ const data = await apiRequest("/api/automation-scripts", { headers });
400
+ const automations = Array.isArray(data) ? data : data.data || [];
401
+ if (!automations.length) {
402
+ console.log(chalk.gray(" No hay automation scripts en esta company.\n"));
403
+ return;
404
+ }
405
+ console.log(chalk.gray(" ID\tNombre\t\t\t\tDescripción"));
406
+ console.log(chalk.gray(" ─".repeat(45)));
407
+ for (const auto of automations) {
408
+ const id = String(auto.id).padEnd(4);
409
+ const name = (auto.name || "").substring(0, 28).padEnd(28);
410
+ const desc = (auto.description || "-").substring(0, 30);
411
+ console.log(` ${chalk.cyan(id)}\t${name}\t${desc}`);
412
+ }
413
+ console.log(chalk.gray("\n 💡 Usa: gufi automation <nombre> para ver/editar el código\n"));
414
+ }
415
+ catch (err) {
416
+ console.log(chalk.red(`\n ✗ Error: ${err.message}\n`));
417
+ }
418
+ }
419
+ // ════════════════════════════════════════════════════════════════════
420
+ // gufi automation <name> [--edit] - View/edit automation code by name
421
+ // ════════════════════════════════════════════════════════════════════
422
+ export async function automationCommand(automationName, options) {
423
+ if (!isLoggedIn()) {
424
+ console.log(chalk.red("\n ✗ No estás logueado. Usa: gufi login\n"));
425
+ process.exit(1);
426
+ }
427
+ if (!automationName) {
428
+ console.log(chalk.red("\n ✗ Uso: gufi automation <nombre> [--edit] [--company <id>]\n"));
429
+ console.log(chalk.gray(" Tip: Usa 'gufi automations' para ver la lista\n"));
430
+ process.exit(1);
431
+ }
432
+ console.log(chalk.magenta(`\n 🟣 Automation: ${automationName}\n`));
433
+ try {
434
+ const headers = {};
435
+ if (options?.company) {
436
+ headers["X-Company-ID"] = options.company;
437
+ }
438
+ // Get all scripts and find by name
439
+ const data = await apiRequest("/api/automation-scripts", { headers });
440
+ const automations = Array.isArray(data) ? data : data.data || [];
441
+ const automation = automations.find((a) => a.name === automationName || String(a.id) === automationName);
442
+ if (!automation) {
443
+ console.log(chalk.red(` ✗ Automation "${automationName}" no encontrada\n`));
444
+ console.log(chalk.gray(" 💡 Usa: gufi automations para ver la lista\n"));
445
+ return;
446
+ }
447
+ // Show info
448
+ console.log(chalk.white(` Nombre: ${automation.name}`));
449
+ console.log(chalk.white(` ID: ${automation.id}`));
450
+ if (automation.description) {
451
+ console.log(chalk.white(` Descripción: ${automation.description}`));
452
+ }
453
+ console.log();
454
+ const jsCode = automation.code || "";
455
+ if (options?.edit) {
456
+ await editAutomationCode(automation.name, jsCode, options.company);
457
+ }
458
+ else if (options?.file) {
459
+ const fs = await import("fs");
460
+ fs.writeFileSync(options.file, jsCode);
461
+ console.log(chalk.green(` ✓ Código guardado en: ${options.file}\n`));
462
+ }
463
+ else {
464
+ console.log(chalk.gray(" Código JavaScript:"));
465
+ console.log(chalk.gray(" ─".repeat(30)));
466
+ console.log(jsCode || "(vacío)");
467
+ console.log(chalk.gray("\n 💡 Usa: gufi automation <nombre> --edit para editar\n"));
468
+ }
469
+ }
470
+ catch (err) {
471
+ console.log(chalk.red(`\n ✗ Error: ${err.message}\n`));
472
+ }
473
+ }
474
+ async function editAutomationCode(automationName, currentCode, companyId) {
475
+ const fs = await import("fs");
476
+ const os = await import("os");
477
+ const path = await import("path");
478
+ const { spawn } = await import("child_process");
479
+ // Safe filename
480
+ const safeName = automationName.replace(/[^a-zA-Z0-9_-]/g, "_");
481
+ const tempFile = path.join(os.tmpdir(), `gufi-automation-${safeName}.js`);
482
+ fs.writeFileSync(tempFile, currentCode);
483
+ console.log(chalk.gray(` Abriendo editor... (${tempFile})`));
484
+ const editor = process.env.EDITOR || process.env.VISUAL || "nano";
485
+ const child = spawn(editor, [tempFile], { stdio: "inherit", shell: true });
486
+ await new Promise((resolve, reject) => {
487
+ child.on("close", (code) => code === 0 ? resolve() : reject(new Error(`Editor exit ${code}`)));
488
+ child.on("error", reject);
489
+ });
490
+ const newCode = fs.readFileSync(tempFile, "utf-8");
491
+ if (newCode === currentCode) {
492
+ console.log(chalk.gray(" Sin cambios.\n"));
493
+ fs.unlinkSync(tempFile);
494
+ return;
495
+ }
496
+ console.log(chalk.yellow(" ⏳ Guardando cambios..."));
497
+ try {
498
+ const headers = {};
499
+ if (companyId)
500
+ headers["X-Company-ID"] = companyId;
501
+ await apiRequest(`/api/automation-scripts/${encodeURIComponent(automationName)}`, {
502
+ method: "PUT",
503
+ headers,
504
+ body: JSON.stringify({ code: newCode }),
505
+ });
506
+ console.log(chalk.green("\n ✓ Automation actualizada!\n"));
507
+ fs.unlinkSync(tempFile);
508
+ }
509
+ catch (err) {
510
+ console.log(chalk.red(`\n ✗ Error: ${err.message}`));
511
+ console.log(chalk.gray(` Archivo temporal: ${tempFile}\n`));
512
+ }
513
+ }
@@ -0,0 +1,8 @@
1
+ /**
2
+ * gufi logs - Stream console logs from LivePreview in real-time
3
+ *
4
+ * The CLI connects to the Gufi WebSocket server and subscribes to
5
+ * dev-logs for a specific view. When the LivePreview is open in the
6
+ * browser, console logs are forwarded to the CLI.
7
+ */
8
+ export declare function logsCommand(viewDir?: string): Promise<void>;