gufi-cli 0.1.3 → 0.1.4

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,6 +1419,36 @@ 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
@@ -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,6 +38,7 @@ 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")
@@ -114,4 +124,45 @@ program
114
124
  .option("-c, --company <id>", "Company ID (if not default)")
115
125
  .option("-f, --file <path>", "Save code to file")
116
126
  .action(automationCommand);
127
+ // ════════════════════════════════════════════════════════════════════
128
+ // Row CRUD Commands - Create, Read, Update, Delete rows
129
+ // ════════════════════════════════════════════════════════════════════
130
+ program
131
+ .command("rows <table>")
132
+ .description("List rows from a table")
133
+ .option("-l, --limit <n>", "Number of rows to fetch", "20")
134
+ .option("-o, --offset <n>", "Starting offset", "0")
135
+ .option("-s, --sort <field>", "Sort by field", "id")
136
+ .option("--order <dir>", "Sort order (ASC/DESC)", "DESC")
137
+ .option("-f, --filter <expr>", "Filter expression (field=value)")
138
+ .action(rowsListCommand);
139
+ program
140
+ .command("row <table> <id>")
141
+ .description("Get a single row by ID")
142
+ .action(rowGetCommand);
143
+ program
144
+ .command("row:create <table>")
145
+ .description("Create a new row")
146
+ .option("-d, --data <json>", "JSON data for the row")
147
+ .option("-f, --file <path>", "JSON file with row data")
148
+ .action(rowCreateCommand);
149
+ program
150
+ .command("row:update <table> <id>")
151
+ .description("Update a row by ID")
152
+ .option("-d, --data <json>", "JSON data to update")
153
+ .option("-f, --file <path>", "JSON file with update data")
154
+ .action(rowUpdateCommand);
155
+ program
156
+ .command("row:delete <table> <id>")
157
+ .description("Delete a row by ID")
158
+ .action(rowDeleteCommand);
159
+ program
160
+ .command("row:duplicate <table> <id>")
161
+ .description("Duplicate an existing row")
162
+ .action(rowDuplicateCommand);
163
+ program
164
+ .command("rows:create <table>")
165
+ .description("Bulk create rows from JSON file")
166
+ .option("-f, --file <path>", "JSON file with array of rows")
167
+ .action(rowsBulkCreateCommand);
117
168
  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.4",
4
4
  "description": "CLI for developing Gufi Marketplace views locally with Claude Code",
5
5
  "bin": {
6
6
  "gufi": "./bin/gufi.js"