gufi-cli 0.1.3 → 0.1.6

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/CLAUDE.md CHANGED
@@ -1419,26 +1419,56 @@ gufi automation calcular_stock -c 116 --edit
1419
1419
  gufi automation calcular_stock -c 116 --file script.js
1420
1420
  ```
1421
1421
 
1422
+ ### Row CRUD (Datos de Tablas)
1423
+
1424
+ ```bash
1425
+ # Listar registros de una tabla
1426
+ gufi rows m360_t16192 # Últimos 20 registros
1427
+ gufi rows m360_t16192 -l 50 # Últimos 50 registros
1428
+ gufi rows m360_t16192 -f estado=activo # Filtrar por campo
1429
+
1430
+ # Ver un registro específico
1431
+ gufi row m360_t16192 123
1432
+
1433
+ # Crear registro
1434
+ gufi row:create m360_t16192 --data '{"nombre":"Test","estado":"activo"}'
1435
+ gufi row:create m360_t16192 --file nuevo.json
1436
+
1437
+ # Actualizar registro
1438
+ gufi row:update m360_t16192 123 --data '{"estado":"completado"}'
1439
+ gufi row:update m360_t16192 123 --file cambios.json
1440
+
1441
+ # Eliminar registro
1442
+ gufi row:delete m360_t16192 123
1443
+
1444
+ # Duplicar registro (copia sin id/timestamps)
1445
+ gufi row:duplicate m360_t16192 123
1446
+
1447
+ # Crear múltiples registros desde archivo JSON
1448
+ gufi rows:create m360_t16192 --file datos.json
1449
+ # datos.json debe ser un array: [{"nombre":"A"},{"nombre":"B"}]
1450
+ ```
1451
+
1422
1452
  ### Desarrollo de Views
1423
1453
 
1424
1454
  ```bash
1425
- # Ver tus packages y views
1426
- gufi list
1455
+ # Ver tus views del Marketplace
1456
+ gufi views
1427
1457
 
1428
1458
  # Descargar view para editar localmente
1429
- gufi pull "Stock Overview"
1459
+ gufi view:pull "Stock Overview"
1430
1460
 
1431
1461
  # Auto-sync al guardar archivos
1432
- gufi watch
1462
+ gufi view:watch
1433
1463
 
1434
1464
  # Ver console.log del LivePreview
1435
- gufi logs
1465
+ gufi view:logs
1436
1466
 
1437
1467
  # Subir cambios manualmente
1438
- gufi push
1468
+ gufi view:push
1439
1469
 
1440
1470
  # Ver estado de sincronización
1441
- gufi status
1471
+ gufi view:status
1442
1472
  ```
1443
1473
 
1444
1474
  ### Opciones Globales
@@ -0,0 +1,46 @@
1
+ /**
2
+ * gufi rows - CRUD operations on table rows
3
+ * For quick testing and data manipulation
4
+ */
5
+ interface RowsOptions {
6
+ company?: string;
7
+ limit?: string;
8
+ offset?: string;
9
+ sort?: string;
10
+ order?: string;
11
+ filter?: string;
12
+ }
13
+ interface RowOptions {
14
+ company?: string;
15
+ data?: string;
16
+ file?: string;
17
+ }
18
+ /**
19
+ * gufi rows <table> - List rows from a table
20
+ */
21
+ export declare function rowsListCommand(table: string, options: RowsOptions): Promise<void>;
22
+ /**
23
+ * gufi row <table> <id> - Get a single row
24
+ */
25
+ export declare function rowGetCommand(table: string, id: string, options: RowOptions): Promise<void>;
26
+ /**
27
+ * gufi row:create <table> --data '{...}' - Create a row
28
+ */
29
+ export declare function rowCreateCommand(table: string, options: RowOptions): Promise<void>;
30
+ /**
31
+ * gufi row:update <table> <id> --data '{...}' - Update a row
32
+ */
33
+ export declare function rowUpdateCommand(table: string, id: string, options: RowOptions): Promise<void>;
34
+ /**
35
+ * gufi row:delete <table> <id> - Delete a row
36
+ */
37
+ export declare function rowDeleteCommand(table: string, id: string, options: RowOptions): Promise<void>;
38
+ /**
39
+ * gufi row:duplicate <table> <id> - Duplicate a row
40
+ */
41
+ export declare function rowDuplicateCommand(table: string, id: string, options: RowOptions): Promise<void>;
42
+ /**
43
+ * gufi rows:create <table> --file datos.json - Bulk create rows
44
+ */
45
+ export declare function rowsBulkCreateCommand(table: string, options: RowOptions): Promise<void>;
46
+ export {};
@@ -0,0 +1,243 @@
1
+ /**
2
+ * gufi rows - CRUD operations on table rows
3
+ * For quick testing and data manipulation
4
+ */
5
+ import chalk from "chalk";
6
+ import ora from "ora";
7
+ import fs from "fs";
8
+ import { getToken, getApiUrl } from "../lib/config.js";
9
+ async function apiRequest(endpoint, options = {}) {
10
+ const token = getToken();
11
+ if (!token) {
12
+ throw new Error("No estás logueado. Ejecuta: gufi login");
13
+ }
14
+ const url = `${getApiUrl()}${endpoint}`;
15
+ const response = await fetch(url, {
16
+ ...options,
17
+ headers: {
18
+ "Content-Type": "application/json",
19
+ Authorization: `Bearer ${token}`,
20
+ "X-Client": "cli",
21
+ ...options.headers,
22
+ },
23
+ });
24
+ if (!response.ok) {
25
+ const text = await response.text();
26
+ throw new Error(`API Error ${response.status}: ${text}`);
27
+ }
28
+ return response.json();
29
+ }
30
+ /**
31
+ * gufi rows <table> - List rows from a table
32
+ */
33
+ export async function rowsListCommand(table, options) {
34
+ const spinner = ora("Cargando registros...").start();
35
+ try {
36
+ const limit = options.limit || "20";
37
+ const offset = options.offset || "0";
38
+ const sort = options.sort || "id";
39
+ const order = options.order || "DESC";
40
+ let endpoint = `/api/tables/${table}?_start=${offset}&_end=${parseInt(offset) + parseInt(limit)}&_sort=${sort}&_order=${order}`;
41
+ if (options.filter) {
42
+ // Parse filter like "estado=pendiente"
43
+ const [field, value] = options.filter.split("=");
44
+ if (field && value) {
45
+ endpoint += `&${field}=${encodeURIComponent(value)}`;
46
+ }
47
+ }
48
+ const data = await apiRequest(endpoint);
49
+ spinner.stop();
50
+ console.log(chalk.magenta(`\n 📋 ${table}\n`));
51
+ if (!data || data.length === 0) {
52
+ console.log(chalk.gray(" No hay registros\n"));
53
+ return;
54
+ }
55
+ // Show as table
56
+ const rows = Array.isArray(data) ? data : [data];
57
+ // Get columns from first row
58
+ const columns = Object.keys(rows[0]).slice(0, 6); // Max 6 columns
59
+ // Header
60
+ console.log(chalk.gray(" " + columns.map((c) => c.padEnd(15)).join(" ")));
61
+ console.log(chalk.gray(" " + "─".repeat(columns.length * 16)));
62
+ // Rows
63
+ for (const row of rows) {
64
+ const values = columns.map((col) => {
65
+ let val = row[col];
66
+ if (val === null || val === undefined)
67
+ val = "-";
68
+ if (typeof val === "object")
69
+ val = JSON.stringify(val).slice(0, 12);
70
+ return String(val).slice(0, 14).padEnd(15);
71
+ });
72
+ console.log(" " + values.join(" "));
73
+ }
74
+ console.log(chalk.gray(`\n Total: ${rows.length} registros\n`));
75
+ }
76
+ catch (error) {
77
+ spinner.fail(chalk.red(error.message));
78
+ process.exit(1);
79
+ }
80
+ }
81
+ /**
82
+ * gufi row <table> <id> - Get a single row
83
+ */
84
+ export async function rowGetCommand(table, id, options) {
85
+ const spinner = ora("Cargando registro...").start();
86
+ try {
87
+ const data = await apiRequest(`/api/tables/${table}/${id}`);
88
+ spinner.stop();
89
+ console.log(chalk.magenta(`\n 📄 ${table} #${id}\n`));
90
+ console.log(JSON.stringify(data, null, 2));
91
+ console.log();
92
+ }
93
+ catch (error) {
94
+ spinner.fail(chalk.red(error.message));
95
+ process.exit(1);
96
+ }
97
+ }
98
+ /**
99
+ * gufi row:create <table> --data '{...}' - Create a row
100
+ */
101
+ export async function rowCreateCommand(table, options) {
102
+ const spinner = ora("Creando registro...").start();
103
+ try {
104
+ let data;
105
+ if (options.file) {
106
+ const content = fs.readFileSync(options.file, "utf-8");
107
+ data = JSON.parse(content);
108
+ }
109
+ else if (options.data) {
110
+ data = JSON.parse(options.data);
111
+ }
112
+ else {
113
+ spinner.fail(chalk.red("Usa --data '{...}' o --file datos.json"));
114
+ process.exit(1);
115
+ }
116
+ const result = await apiRequest(`/api/tables/${table}`, {
117
+ method: "POST",
118
+ body: JSON.stringify(data),
119
+ });
120
+ spinner.succeed(chalk.green(`Registro creado: ID ${result.id || result.data?.id}`));
121
+ console.log(chalk.gray(JSON.stringify(result.data || result, null, 2)));
122
+ console.log();
123
+ }
124
+ catch (error) {
125
+ spinner.fail(chalk.red(error.message));
126
+ process.exit(1);
127
+ }
128
+ }
129
+ /**
130
+ * gufi row:update <table> <id> --data '{...}' - Update a row
131
+ */
132
+ export async function rowUpdateCommand(table, id, options) {
133
+ const spinner = ora("Actualizando registro...").start();
134
+ try {
135
+ let data;
136
+ if (options.file) {
137
+ const content = fs.readFileSync(options.file, "utf-8");
138
+ data = JSON.parse(content);
139
+ }
140
+ else if (options.data) {
141
+ data = JSON.parse(options.data);
142
+ }
143
+ else {
144
+ spinner.fail(chalk.red("Usa --data '{...}' o --file datos.json"));
145
+ process.exit(1);
146
+ }
147
+ const result = await apiRequest(`/api/tables/${table}/${id}`, {
148
+ method: "PUT",
149
+ body: JSON.stringify(data),
150
+ });
151
+ spinner.succeed(chalk.green(`Registro #${id} actualizado`));
152
+ console.log(chalk.gray(JSON.stringify(result.data || result, null, 2)));
153
+ console.log();
154
+ }
155
+ catch (error) {
156
+ spinner.fail(chalk.red(error.message));
157
+ process.exit(1);
158
+ }
159
+ }
160
+ /**
161
+ * gufi row:delete <table> <id> - Delete a row
162
+ */
163
+ export async function rowDeleteCommand(table, id, options) {
164
+ const spinner = ora("Eliminando registro...").start();
165
+ try {
166
+ await apiRequest(`/api/tables/${table}/${id}`, {
167
+ method: "DELETE",
168
+ });
169
+ spinner.succeed(chalk.green(`Registro #${id} eliminado`));
170
+ console.log();
171
+ }
172
+ catch (error) {
173
+ spinner.fail(chalk.red(error.message));
174
+ process.exit(1);
175
+ }
176
+ }
177
+ /**
178
+ * gufi row:duplicate <table> <id> - Duplicate a row
179
+ */
180
+ export async function rowDuplicateCommand(table, id, options) {
181
+ const spinner = ora("Duplicando registro...").start();
182
+ try {
183
+ // Get original row
184
+ const original = await apiRequest(`/api/tables/${table}/${id}`);
185
+ // Remove id and timestamps
186
+ const { id: _, created_at, updated_at, ...data } = original;
187
+ // Create new row
188
+ const result = await apiRequest(`/api/tables/${table}`, {
189
+ method: "POST",
190
+ body: JSON.stringify(data),
191
+ });
192
+ spinner.succeed(chalk.green(`Registro duplicado: #${id} → #${result.id || result.data?.id}`));
193
+ console.log();
194
+ }
195
+ catch (error) {
196
+ spinner.fail(chalk.red(error.message));
197
+ process.exit(1);
198
+ }
199
+ }
200
+ /**
201
+ * gufi rows:create <table> --file datos.json - Bulk create rows
202
+ */
203
+ export async function rowsBulkCreateCommand(table, options) {
204
+ if (!options.file) {
205
+ console.log(chalk.red("\n ✗ Usa --file datos.json\n"));
206
+ process.exit(1);
207
+ }
208
+ const spinner = ora("Creando registros...").start();
209
+ try {
210
+ const content = fs.readFileSync(options.file, "utf-8");
211
+ const rows = JSON.parse(content);
212
+ if (!Array.isArray(rows)) {
213
+ spinner.fail(chalk.red("El archivo debe contener un array de objetos"));
214
+ process.exit(1);
215
+ }
216
+ let created = 0;
217
+ let errors = 0;
218
+ for (const row of rows) {
219
+ try {
220
+ await apiRequest(`/api/tables/${table}`, {
221
+ method: "POST",
222
+ body: JSON.stringify(row),
223
+ });
224
+ created++;
225
+ spinner.text = `Creando registros... ${created}/${rows.length}`;
226
+ }
227
+ catch {
228
+ errors++;
229
+ }
230
+ }
231
+ if (errors === 0) {
232
+ spinner.succeed(chalk.green(`${created} registros creados`));
233
+ }
234
+ else {
235
+ spinner.warn(chalk.yellow(`${created} creados, ${errors} errores`));
236
+ }
237
+ console.log();
238
+ }
239
+ catch (error) {
240
+ spinner.fail(chalk.red(error.message));
241
+ process.exit(1);
242
+ }
243
+ }
package/dist/index.d.ts CHANGED
@@ -20,5 +20,14 @@
20
20
  * gufi company:create Create a new company
21
21
  * gufi automations List automation scripts
22
22
  * gufi automation <name> View/edit automation code (--edit, --file)
23
+ *
24
+ * Row CRUD Commands:
25
+ * gufi rows <table> List rows from a table
26
+ * gufi row <table> <id> Get a single row
27
+ * gufi row:create <table> Create a new row (--data or --file)
28
+ * gufi row:update <table> <id> Update a row (--data or --file)
29
+ * gufi row:delete <table> <id> Delete a row
30
+ * gufi row:duplicate <table> <id> Duplicate a row
31
+ * gufi rows:create <table> Bulk create from JSON file
23
32
  */
24
33
  export {};
package/dist/index.js CHANGED
@@ -20,6 +20,15 @@
20
20
  * gufi company:create Create a new company
21
21
  * gufi automations List automation scripts
22
22
  * gufi automation <name> View/edit automation code (--edit, --file)
23
+ *
24
+ * Row CRUD Commands:
25
+ * gufi rows <table> List rows from a table
26
+ * gufi row <table> <id> Get a single row
27
+ * gufi row:create <table> Create a new row (--data or --file)
28
+ * gufi row:update <table> <id> Update a row (--data or --file)
29
+ * gufi row:delete <table> <id> Delete a row
30
+ * gufi row:duplicate <table> <id> Duplicate a row
31
+ * gufi rows:create <table> Bulk create from JSON file
23
32
  */
24
33
  import { Command } from "commander";
25
34
  import { loginCommand, logoutCommand, whoamiCommand } from "./commands/login.js";
@@ -29,89 +38,137 @@ import { watchCommand } from "./commands/watch.js";
29
38
  import { listCommand } from "./commands/list.js";
30
39
  import { logsCommand } from "./commands/logs.js";
31
40
  import { companiesCommand, modulesCommand, moduleCommand, moduleUpdateCommand, companyCreateCommand, automationsCommand, automationCommand, } from "./commands/companies.js";
41
+ import { rowsListCommand, rowGetCommand, rowCreateCommand, rowUpdateCommand, rowDeleteCommand, rowDuplicateCommand, rowsBulkCreateCommand, } from "./commands/rows.js";
32
42
  const program = new Command();
33
43
  program
34
44
  .name("gufi")
35
- .description("Gufi Developer CLI - Develop Gufi views locally")
36
- .version("0.1.0");
37
- // Auth commands
45
+ .description("🟣 Gufi CLI - Desarrolla módulos, vistas y automations")
46
+ .version("0.1.6");
47
+ // ════════════════════════════════════════════════════════════════════
48
+ // 🔐 Auth
49
+ // ════════════════════════════════════════════════════════════════════
38
50
  program
39
51
  .command("login")
40
- .description("Login to Gufi")
41
- .option("--api <url>", "Custom API URL")
52
+ .description("Iniciar sesión en Gufi")
53
+ .option("--api <url>", "URL de API personalizada")
42
54
  .action(loginCommand);
43
55
  program
44
56
  .command("logout")
45
- .description("Logout from Gufi")
57
+ .description("Cerrar sesión")
46
58
  .action(logoutCommand);
47
59
  program
48
60
  .command("whoami")
49
- .description("Show current logged in user")
61
+ .description("Ver usuario actual")
50
62
  .action(whoamiCommand);
51
- // Sync commands
52
- program
53
- .command("pull [view]")
54
- .description("Download view files from Gufi")
55
- .action(pullCommand);
56
- program
57
- .command("push")
58
- .description("Upload local changes to Gufi")
59
- .action(pushCommand);
60
- program
61
- .command("watch [dir]")
62
- .description("Watch for file changes and auto-sync")
63
- .action(watchCommand);
64
- program
65
- .command("status")
66
- .description("Show sync status")
67
- .action(statusCommand);
68
- program
69
- .command("list")
70
- .alias("ls")
71
- .description("List your packages and views")
72
- .action(listCommand);
73
- program
74
- .command("logs [dir]")
75
- .description("Stream console logs from LivePreview in real-time")
76
- .action(logsCommand);
77
63
  // ════════════════════════════════════════════════════════════════════
78
- // SDK Commands - Companies, Modules, Automations
64
+ // 🏢 Companies & Modules
79
65
  // ════════════════════════════════════════════════════════════════════
80
66
  program
81
67
  .command("companies")
82
- .description("List your companies")
68
+ .description("Ver mis companies")
83
69
  .action(companiesCommand);
70
+ program
71
+ .command("company:create <name>")
72
+ .description("Crear nueva company")
73
+ .action(companyCreateCommand);
84
74
  program
85
75
  .command("modules <company_id>")
86
- .description("List modules of a company")
76
+ .description("Ver módulos de una company")
87
77
  .action(modulesCommand);
88
78
  program
89
79
  .command("module <module_id>")
90
- .description("View or edit a module JSON")
91
- .option("-e, --edit", "Open in editor to edit")
92
- .option("-c, --company <id>", "Company ID (if not default)")
93
- .option("-f, --file <path>", "Save JSON to file")
80
+ .description("Ver/editar JSON de un módulo")
81
+ .option("-e, --edit", "Abrir en editor")
82
+ .option("-c, --company <id>", "ID de company")
83
+ .option("-f, --file <path>", "Exportar a archivo")
94
84
  .action(moduleCommand);
95
85
  program
96
86
  .command("module:update <module_id> <json_file>")
97
- .description("Update module from a JSON file")
98
- .option("-c, --company <id>", "Company ID (if not default)")
99
- .option("--dry-run", "Validate without saving")
87
+ .description("Actualizar módulo desde archivo JSON")
88
+ .option("-c, --company <id>", "ID de company")
89
+ .option("--dry-run", "Validar sin guardar")
100
90
  .action(moduleUpdateCommand);
101
- program
102
- .command("company:create <name>")
103
- .description("Create a new company")
104
- .action(companyCreateCommand);
91
+ // ════════════════════════════════════════════════════════════════════
92
+ // ⚡ Automations
93
+ // ════════════════════════════════════════════════════════════════════
105
94
  program
106
95
  .command("automations")
107
- .description("List all automation scripts")
108
- .option("-c, --company <id>", "Company ID (if not default)")
96
+ .description("Ver scripts de automation")
97
+ .option("-c, --company <id>", "ID de company")
109
98
  .action(automationsCommand);
110
99
  program
111
100
  .command("automation <name>")
112
- .description("View or edit an automation by name")
113
- .option("-e, --edit", "Open in editor to edit")
114
- .option("-c, --company <id>", "Company ID (if not default)")
115
- .option("-f, --file <path>", "Save code to file")
101
+ .description("Ver/editar código de automation")
102
+ .option("-e, --edit", "Abrir en editor")
103
+ .option("-c, --company <id>", "ID de company")
104
+ .option("-f, --file <path>", "Exportar a archivo")
116
105
  .action(automationCommand);
106
+ // ════════════════════════════════════════════════════════════════════
107
+ // 📊 Row CRUD - Datos de tablas
108
+ // ════════════════════════════════════════════════════════════════════
109
+ program
110
+ .command("rows <table>")
111
+ .description("Listar registros (ej: gufi rows m360_t16192)")
112
+ .option("-l, --limit <n>", "Cantidad", "20")
113
+ .option("-o, --offset <n>", "Desde", "0")
114
+ .option("-s, --sort <field>", "Ordenar por", "id")
115
+ .option("--order <dir>", "ASC/DESC", "DESC")
116
+ .option("-f, --filter <expr>", "Filtro (campo=valor)")
117
+ .action(rowsListCommand);
118
+ program
119
+ .command("row <table> <id>")
120
+ .description("Ver un registro por ID")
121
+ .action(rowGetCommand);
122
+ program
123
+ .command("row:create <table>")
124
+ .description("Crear registro")
125
+ .option("-d, --data <json>", "JSON con datos")
126
+ .option("-f, --file <path>", "Archivo JSON")
127
+ .action(rowCreateCommand);
128
+ program
129
+ .command("row:update <table> <id>")
130
+ .description("Actualizar registro")
131
+ .option("-d, --data <json>", "JSON con cambios")
132
+ .option("-f, --file <path>", "Archivo JSON")
133
+ .action(rowUpdateCommand);
134
+ program
135
+ .command("row:delete <table> <id>")
136
+ .description("Eliminar registro")
137
+ .action(rowDeleteCommand);
138
+ program
139
+ .command("row:duplicate <table> <id>")
140
+ .description("Duplicar registro existente")
141
+ .action(rowDuplicateCommand);
142
+ program
143
+ .command("rows:create <table>")
144
+ .description("Crear múltiples registros desde JSON")
145
+ .option("-f, --file <path>", "Archivo con array de objetos")
146
+ .action(rowsBulkCreateCommand);
147
+ // ════════════════════════════════════════════════════════════════════
148
+ // 🎨 Views (Marketplace) - Desarrollo local
149
+ // ════════════════════════════════════════════════════════════════════
150
+ program
151
+ .command("views")
152
+ .description("Ver mis views del Marketplace")
153
+ .action(listCommand);
154
+ program
155
+ .command("view:pull [name]")
156
+ .description("Descargar vista para editar localmente")
157
+ .action(pullCommand);
158
+ program
159
+ .command("view:push")
160
+ .description("Subir cambios locales a Gufi")
161
+ .action(pushCommand);
162
+ program
163
+ .command("view:watch [dir]")
164
+ .description("Auto-sync al guardar archivos")
165
+ .action(watchCommand);
166
+ program
167
+ .command("view:status")
168
+ .description("Ver estado de sincronización")
169
+ .action(statusCommand);
170
+ program
171
+ .command("view:logs [dir]")
172
+ .description("Ver console.log del LivePreview")
173
+ .action(logsCommand);
117
174
  program.parse();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "gufi-cli",
3
- "version": "0.1.3",
3
+ "version": "0.1.6",
4
4
  "description": "CLI for developing Gufi Marketplace views locally with Claude Code",
5
5
  "bin": {
6
6
  "gufi": "./bin/gufi.js"