gufi-cli 0.1.13 β†’ 0.1.17

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.
@@ -6,6 +6,8 @@ import chalk from "chalk";
6
6
  import ora from "ora";
7
7
  import fs from "fs";
8
8
  import { getToken, getApiUrl, loadConfig, getRefreshToken, setToken } from "../lib/config.js";
9
+ // πŸ’œ Cache for module β†’ company mapping (avoid repeated lookups)
10
+ const moduleCompanyCache = new Map();
9
11
  // πŸ’œ Auto-login with saved credentials
10
12
  async function autoLogin() {
11
13
  const config = loadConfig();
@@ -48,7 +50,7 @@ async function refreshOrLogin() {
48
50
  return autoLogin();
49
51
  }
50
52
  // πŸ’œ API request with auto-login and refresh
51
- async function apiRequest(endpoint, options = {}, retry = true) {
53
+ async function apiRequest(endpoint, options = {}, companyId, retry = true) {
52
54
  let token = getToken();
53
55
  if (!token) {
54
56
  token = await autoLogin();
@@ -56,19 +58,26 @@ async function apiRequest(endpoint, options = {}, retry = true) {
56
58
  throw new Error("No estΓ‘s logueado. Ejecuta: gufi login");
57
59
  }
58
60
  const url = `${getApiUrl()}${endpoint}`;
61
+ const headers = {
62
+ "Content-Type": "application/json",
63
+ Authorization: `Bearer ${token}`,
64
+ "X-Client": "cli",
65
+ };
66
+ // Add company header if specified
67
+ if (companyId) {
68
+ headers["X-Company-ID"] = companyId;
69
+ }
59
70
  const response = await fetch(url, {
60
71
  ...options,
61
72
  headers: {
62
- "Content-Type": "application/json",
63
- Authorization: `Bearer ${token}`,
64
- "X-Client": "cli",
73
+ ...headers,
65
74
  ...options.headers,
66
75
  },
67
76
  });
68
77
  if ((response.status === 401 || response.status === 403) && retry) {
69
78
  const newToken = await refreshOrLogin();
70
79
  if (newToken)
71
- return apiRequest(endpoint, options, false);
80
+ return apiRequest(endpoint, options, companyId, false);
72
81
  }
73
82
  if (!response.ok) {
74
83
  const text = await response.text();
@@ -76,35 +85,109 @@ async function apiRequest(endpoint, options = {}, retry = true) {
76
85
  }
77
86
  return response.json();
78
87
  }
88
+ // πŸ’œ Parse module ID from table name (e.g., "m308_t4136" β†’ 308)
89
+ function parseModuleId(table) {
90
+ const match = table.match(/^m(\d+)_t\d+$/);
91
+ return match ? parseInt(match[1], 10) : null;
92
+ }
93
+ // πŸ’œ Detect company from table/module name (auto-switch company)
94
+ async function detectCompanyFromTable(table) {
95
+ const moduleId = parseModuleId(table);
96
+ if (!moduleId)
97
+ return undefined;
98
+ // Check cache first
99
+ if (moduleCompanyCache.has(moduleId)) {
100
+ return moduleCompanyCache.get(moduleId);
101
+ }
102
+ try {
103
+ // Get all companies the user has access to
104
+ const companiesData = await apiRequest("/api/companies");
105
+ const companies = companiesData.items || companiesData.data || companiesData || [];
106
+ // Search through each company's schema for the module
107
+ for (const company of companies) {
108
+ try {
109
+ const schemaData = await apiRequest("/api/company/schema", {
110
+ headers: { "X-Company-ID": String(company.id) },
111
+ });
112
+ const modules = schemaData.modules || schemaData.data?.modules || [];
113
+ // Check if any entity in any submodule has this moduleId
114
+ for (const mod of modules) {
115
+ for (const sub of mod.submodules || []) {
116
+ for (const ent of sub.entities || []) {
117
+ if (ent.ui?.moduleId === moduleId) {
118
+ // Found! Cache and return
119
+ const companyIdStr = String(company.id);
120
+ moduleCompanyCache.set(moduleId, companyIdStr);
121
+ return companyIdStr;
122
+ }
123
+ }
124
+ }
125
+ }
126
+ }
127
+ catch {
128
+ // Skip companies we can't access
129
+ continue;
130
+ }
131
+ }
132
+ }
133
+ catch {
134
+ // If we can't get companies, return undefined
135
+ return undefined;
136
+ }
137
+ return undefined;
138
+ }
139
+ // πŸ’œ Get company ID - use provided or auto-detect from table
140
+ async function resolveCompanyId(table, providedCompanyId) {
141
+ if (providedCompanyId)
142
+ return providedCompanyId;
143
+ const detected = await detectCompanyFromTable(table);
144
+ if (detected) {
145
+ console.log(chalk.gray(` [gufi] Auto-detected company: ${detected} (from module ${parseModuleId(table)})`));
146
+ }
147
+ return detected;
148
+ }
79
149
  /**
80
150
  * gufi rows <table> - List rows from a table
151
+ * Uses POST /api/tables/:table/list
81
152
  */
82
153
  export async function rowsListCommand(table, options) {
83
- const spinner = ora("Cargando registros...").start();
154
+ const spinner = ora("Detectando company...").start();
84
155
  try {
85
- const limit = options.limit || "20";
86
- const offset = options.offset || "0";
156
+ // πŸ’œ Auto-detect company from table name if not provided
157
+ const companyId = await resolveCompanyId(table, options.company);
158
+ spinner.text = "Cargando registros...";
159
+ const limit = parseInt(options.limit || "20");
160
+ const offset = parseInt(options.offset || "0");
87
161
  const sort = options.sort || "id";
88
- const order = options.order || "DESC";
89
- let endpoint = `/api/tables/${table}?_start=${offset}&_end=${parseInt(offset) + parseInt(limit)}&_sort=${sort}&_order=${order}`;
162
+ const order = options.order || "desc";
163
+ // Build request body for POST /api/tables/:table/list
164
+ const body = {
165
+ pagination: { current: Math.floor(offset / limit) + 1, pageSize: limit },
166
+ sorters: [{ field: sort, order }],
167
+ filters: [],
168
+ };
90
169
  if (options.filter) {
91
170
  // Parse filter like "estado=pendiente"
92
171
  const [field, value] = options.filter.split("=");
93
172
  if (field && value) {
94
- endpoint += `&${field}=${encodeURIComponent(value)}`;
173
+ body.filters.push({ field, operator: "eq", value });
95
174
  }
96
175
  }
97
- const data = await apiRequest(endpoint);
176
+ const result = await apiRequest(`/api/tables/${table}/list`, {
177
+ method: "POST",
178
+ body: JSON.stringify(body),
179
+ }, companyId);
98
180
  spinner.stop();
99
181
  console.log(chalk.magenta(`\n πŸ“‹ ${table}\n`));
100
- if (!data || data.length === 0) {
182
+ const rows = result.data || [];
183
+ if (rows.length === 0) {
101
184
  console.log(chalk.gray(" No hay registros\n"));
102
185
  return;
103
186
  }
104
- // Show as table
105
- const rows = Array.isArray(data) ? data : [data];
106
- // Get columns from first row
107
- const columns = Object.keys(rows[0]).slice(0, 6); // Max 6 columns
187
+ // Get columns from first row (max 6, skip __display fields)
188
+ const columns = Object.keys(rows[0])
189
+ .filter(k => !k.includes("__"))
190
+ .slice(0, 6);
108
191
  // Header
109
192
  console.log(chalk.gray(" " + columns.map((c) => c.padEnd(15)).join(" ")));
110
193
  console.log(chalk.gray(" " + "─".repeat(columns.length * 16)));
@@ -120,7 +203,7 @@ export async function rowsListCommand(table, options) {
120
203
  });
121
204
  console.log(" " + values.join(" "));
122
205
  }
123
- console.log(chalk.gray(`\n Total: ${rows.length} registros\n`));
206
+ console.log(chalk.gray(`\n Total: ${rows.length} registros (de ${result.total || "?"})\n`));
124
207
  }
125
208
  catch (error) {
126
209
  spinner.fail(chalk.red(error.message));
@@ -129,12 +212,20 @@ export async function rowsListCommand(table, options) {
129
212
  }
130
213
  /**
131
214
  * gufi row <table> <id> - Get a single row
215
+ * Uses POST /api/tables/:table/getOne
132
216
  */
133
217
  export async function rowGetCommand(table, id, options) {
134
- const spinner = ora("Cargando registro...").start();
218
+ const spinner = ora("Detectando company...").start();
135
219
  try {
136
- const data = await apiRequest(`/api/tables/${table}/${id}`);
220
+ // πŸ’œ Auto-detect company from table name if not provided
221
+ const companyId = await resolveCompanyId(table, options.company);
222
+ spinner.text = "Cargando registro...";
223
+ const result = await apiRequest(`/api/tables/${table}/getOne`, {
224
+ method: "POST",
225
+ body: JSON.stringify({ id: parseInt(id) }),
226
+ }, companyId);
137
227
  spinner.stop();
228
+ const data = result.data || result;
138
229
  console.log(chalk.magenta(`\n πŸ“„ ${table} #${id}\n`));
139
230
  console.log(JSON.stringify(data, null, 2));
140
231
  console.log();
@@ -148,8 +239,11 @@ export async function rowGetCommand(table, id, options) {
148
239
  * gufi row:create <table> --data '{...}' - Create a row
149
240
  */
150
241
  export async function rowCreateCommand(table, options) {
151
- const spinner = ora("Creando registro...").start();
242
+ const spinner = ora("Detectando company...").start();
152
243
  try {
244
+ // πŸ’œ Auto-detect company from table name if not provided
245
+ const companyId = await resolveCompanyId(table, options.company);
246
+ spinner.text = "Creando registro...";
153
247
  let data;
154
248
  if (options.file) {
155
249
  const content = fs.readFileSync(options.file, "utf-8");
@@ -165,7 +259,7 @@ export async function rowCreateCommand(table, options) {
165
259
  const result = await apiRequest(`/api/tables/${table}`, {
166
260
  method: "POST",
167
261
  body: JSON.stringify(data),
168
- });
262
+ }, companyId);
169
263
  spinner.succeed(chalk.green(`Registro creado: ID ${result.id || result.data?.id}`));
170
264
  console.log(chalk.gray(JSON.stringify(result.data || result, null, 2)));
171
265
  console.log();
@@ -179,8 +273,11 @@ export async function rowCreateCommand(table, options) {
179
273
  * gufi row:update <table> <id> --data '{...}' - Update a row
180
274
  */
181
275
  export async function rowUpdateCommand(table, id, options) {
182
- const spinner = ora("Actualizando registro...").start();
276
+ const spinner = ora("Detectando company...").start();
183
277
  try {
278
+ // πŸ’œ Auto-detect company from table name if not provided
279
+ const companyId = await resolveCompanyId(table, options.company);
280
+ spinner.text = "Actualizando registro...";
184
281
  let data;
185
282
  if (options.file) {
186
283
  const content = fs.readFileSync(options.file, "utf-8");
@@ -196,7 +293,7 @@ export async function rowUpdateCommand(table, id, options) {
196
293
  const result = await apiRequest(`/api/tables/${table}/${id}`, {
197
294
  method: "PUT",
198
295
  body: JSON.stringify(data),
199
- });
296
+ }, companyId);
200
297
  spinner.succeed(chalk.green(`Registro #${id} actualizado`));
201
298
  console.log(chalk.gray(JSON.stringify(result.data || result, null, 2)));
202
299
  console.log();
@@ -210,11 +307,14 @@ export async function rowUpdateCommand(table, id, options) {
210
307
  * gufi row:delete <table> <id> - Delete a row
211
308
  */
212
309
  export async function rowDeleteCommand(table, id, options) {
213
- const spinner = ora("Eliminando registro...").start();
310
+ const spinner = ora("Detectando company...").start();
214
311
  try {
312
+ // πŸ’œ Auto-detect company from table name if not provided
313
+ const companyId = await resolveCompanyId(table, options.company);
314
+ spinner.text = "Eliminando registro...";
215
315
  await apiRequest(`/api/tables/${table}/${id}`, {
216
316
  method: "DELETE",
217
- });
317
+ }, companyId);
218
318
  spinner.succeed(chalk.green(`Registro #${id} eliminado`));
219
319
  console.log();
220
320
  }
@@ -227,17 +327,24 @@ export async function rowDeleteCommand(table, id, options) {
227
327
  * gufi row:duplicate <table> <id> - Duplicate a row
228
328
  */
229
329
  export async function rowDuplicateCommand(table, id, options) {
230
- const spinner = ora("Duplicando registro...").start();
330
+ const spinner = ora("Detectando company...").start();
231
331
  try {
232
- // Get original row
233
- const original = await apiRequest(`/api/tables/${table}/${id}`);
332
+ // πŸ’œ Auto-detect company from table name if not provided
333
+ const companyId = await resolveCompanyId(table, options.company);
334
+ spinner.text = "Duplicando registro...";
335
+ // Get original row using getOne
336
+ const originalResult = await apiRequest(`/api/tables/${table}/getOne`, {
337
+ method: "POST",
338
+ body: JSON.stringify({ id: parseInt(id) }),
339
+ }, companyId);
340
+ const original = originalResult.data || originalResult;
234
341
  // Remove id and timestamps
235
- const { id: _, created_at, updated_at, ...data } = original;
342
+ const { id: _id, created_at, updated_at, ...data } = original;
236
343
  // Create new row
237
344
  const result = await apiRequest(`/api/tables/${table}`, {
238
345
  method: "POST",
239
346
  body: JSON.stringify(data),
240
- });
347
+ }, companyId);
241
348
  spinner.succeed(chalk.green(`Registro duplicado: #${id} β†’ #${result.id || result.data?.id}`));
242
349
  console.log();
243
350
  }
@@ -254,8 +361,11 @@ export async function rowsBulkCreateCommand(table, options) {
254
361
  console.log(chalk.red("\n βœ— Usa --file datos.json\n"));
255
362
  process.exit(1);
256
363
  }
257
- const spinner = ora("Creando registros...").start();
364
+ const spinner = ora("Detectando company...").start();
258
365
  try {
366
+ // πŸ’œ Auto-detect company from table name if not provided
367
+ const companyId = await resolveCompanyId(table, options.company);
368
+ spinner.text = "Creando registros...";
259
369
  const content = fs.readFileSync(options.file, "utf-8");
260
370
  const rows = JSON.parse(content);
261
371
  if (!Array.isArray(rows)) {
@@ -269,7 +379,7 @@ export async function rowsBulkCreateCommand(table, options) {
269
379
  await apiRequest(`/api/tables/${table}`, {
270
380
  method: "POST",
271
381
  body: JSON.stringify(row),
272
- });
382
+ }, companyId);
273
383
  created++;
274
384
  spinner.text = `Creando registros... ${created}/${rows.length}`;
275
385
  }
package/dist/index.d.ts CHANGED
@@ -2,33 +2,41 @@
2
2
  /**
3
3
  * Gufi Dev CLI - Main Entry Point
4
4
  *
5
- * Commands:
6
- * gufi login Login to Gufi
7
- * gufi logout Logout from Gufi
5
+ * Auth:
6
+ * gufi login Login (credentials saved for auto-login)
7
+ * gufi logout Logout (keeps credentials)
8
8
  * gufi whoami Show current user
9
- * gufi pull [view] Download view files
10
- * gufi push Upload local changes
11
- * gufi watch Auto-sync file changes
12
- * gufi status Show sync status
13
- * gufi logs [dir] Stream console logs from LivePreview
14
9
  *
15
- * SDK Commands:
10
+ * Companies & Modules:
16
11
  * gufi companies List your companies
17
12
  * gufi modules <id> List modules of a company
18
- * gufi module <id> View/edit module JSON (--edit, --file)
13
+ * gufi module <id> View/edit module JSON (auto-detects company)
19
14
  * gufi module:update Update module from JSON file
20
- * gufi module:create Create module from JSON file
21
- * gufi company:create Create a new company
22
- * gufi automations List automation scripts
23
- * gufi automation <name> View/edit automation code (--edit, --file)
24
15
  *
25
- * Row CRUD Commands:
26
- * gufi rows <table> List rows from a table
27
- * gufi row <table> <id> Get a single row
28
- * gufi row:create <table> Create a new row (--data or --file)
29
- * gufi row:update <table> <id> Update a row (--data or --file)
30
- * gufi row:delete <table> <id> Delete a row
31
- * gufi row:duplicate <table> <id> Duplicate a row
32
- * gufi rows:create <table> Bulk create from JSON file
16
+ * Automations:
17
+ * gufi automations List automation scripts
18
+ * gufi automation <id> View/edit automation code
19
+ * gufi automations:meta View worker index (debugging)
20
+ * gufi automations:executions View execution history
21
+ * gufi entity:automations View/edit entity triggers
22
+ *
23
+ * Row CRUD:
24
+ * gufi rows <table> List rows (auto-detects company)
25
+ * gufi row <table> <id> Get/update/delete a row
26
+ *
27
+ * Packages (Marketplace):
28
+ * gufi packages List my packages (with views)
29
+ * gufi package <id> View package details
30
+ * gufi package:create Create new package
31
+ * gufi package:add-module Add module to package
32
+ * gufi package:add-view Add view to package
33
+ * gufi package:publish Publish to Marketplace
34
+ * gufi package:sync Sync version
35
+ *
36
+ * View Development:
37
+ * gufi pull <viewId> Download view for local editing
38
+ * gufi push Upload local changes
39
+ * gufi watch Auto-sync on save
40
+ * gufi logs Console.log from LivePreview
33
41
  */
34
42
  export {};
package/dist/index.js CHANGED
@@ -2,45 +2,56 @@
2
2
  /**
3
3
  * Gufi Dev CLI - Main Entry Point
4
4
  *
5
- * Commands:
6
- * gufi login Login to Gufi
7
- * gufi logout Logout from Gufi
5
+ * Auth:
6
+ * gufi login Login (credentials saved for auto-login)
7
+ * gufi logout Logout (keeps credentials)
8
8
  * gufi whoami Show current user
9
- * gufi pull [view] Download view files
10
- * gufi push Upload local changes
11
- * gufi watch Auto-sync file changes
12
- * gufi status Show sync status
13
- * gufi logs [dir] Stream console logs from LivePreview
14
9
  *
15
- * SDK Commands:
10
+ * Companies & Modules:
16
11
  * gufi companies List your companies
17
12
  * gufi modules <id> List modules of a company
18
- * gufi module <id> View/edit module JSON (--edit, --file)
13
+ * gufi module <id> View/edit module JSON (auto-detects company)
19
14
  * gufi module:update Update module from JSON file
20
- * gufi module:create Create module from JSON file
21
- * gufi company:create Create a new company
22
- * gufi automations List automation scripts
23
- * gufi automation <name> View/edit automation code (--edit, --file)
24
15
  *
25
- * Row CRUD Commands:
26
- * gufi rows <table> List rows from a table
27
- * gufi row <table> <id> Get a single row
28
- * gufi row:create <table> Create a new row (--data or --file)
29
- * gufi row:update <table> <id> Update a row (--data or --file)
30
- * gufi row:delete <table> <id> Delete a row
31
- * gufi row:duplicate <table> <id> Duplicate a row
32
- * gufi rows:create <table> Bulk create from JSON file
16
+ * Automations:
17
+ * gufi automations List automation scripts
18
+ * gufi automation <id> View/edit automation code
19
+ * gufi automations:meta View worker index (debugging)
20
+ * gufi automations:executions View execution history
21
+ * gufi entity:automations View/edit entity triggers
22
+ *
23
+ * Row CRUD:
24
+ * gufi rows <table> List rows (auto-detects company)
25
+ * gufi row <table> <id> Get/update/delete a row
26
+ *
27
+ * Packages (Marketplace):
28
+ * gufi packages List my packages (with views)
29
+ * gufi package <id> View package details
30
+ * gufi package:create Create new package
31
+ * gufi package:add-module Add module to package
32
+ * gufi package:add-view Add view to package
33
+ * gufi package:publish Publish to Marketplace
34
+ * gufi package:sync Sync version
35
+ *
36
+ * View Development:
37
+ * gufi pull <viewId> Download view for local editing
38
+ * gufi push Upload local changes
39
+ * gufi watch Auto-sync on save
40
+ * gufi logs Console.log from LivePreview
33
41
  */
34
42
  import { Command } from "commander";
35
43
  import { loginCommand, logoutCommand, whoamiCommand } from "./commands/login.js";
36
44
  import { pullCommand } from "./commands/pull.js";
37
45
  import { pushCommand, statusCommand } from "./commands/push.js";
38
46
  import { watchCommand } from "./commands/watch.js";
39
- import { listCommand } from "./commands/list.js";
47
+ // listCommand removed - use 'gufi packages' instead
40
48
  import { logsCommand } from "./commands/logs.js";
41
- import { companiesCommand, modulesCommand, moduleCommand, moduleUpdateCommand, moduleCreateCommand, companyCreateCommand, automationsCommand, automationCommand, automationCreateCommand, entityAutomationsCommand, entityAutomationsUpdateCommand, } from "./commands/companies.js";
49
+ import { companiesCommand, modulesCommand, moduleCommand, moduleUpdateCommand, moduleCreateCommand, companyCreateCommand, automationsCommand, automationCommand, automationCreateCommand, entityAutomationsCommand, entityAutomationsUpdateCommand, automationsMetaCommand, automationsExecutionsCommand, } from "./commands/companies.js";
42
50
  import { rowsListCommand, rowGetCommand, rowCreateCommand, rowUpdateCommand, rowDeleteCommand, rowDuplicateCommand, rowsBulkCreateCommand, } from "./commands/rows.js";
43
51
  import { envListCommand, envSetCommand, envDeleteCommand, schemaCommand, } from "./commands/env.js";
52
+ import { packagesCommand, packageCommand, packageCreateCommand, packageDeleteCommand, packageAddModuleCommand, packageRemoveModuleCommand, packagePublishCommand, packageUnpublishCommand, packageSyncCommand, packageCheckCommand,
53
+ // packageViewsCommand removed - use 'gufi package <id>' instead
54
+ packageAddViewCommand, packageRemoveViewCommand, } from "./commands/packages.js";
44
55
  const program = new Command();
45
56
  program
46
57
  .name("gufi")
@@ -86,13 +97,11 @@ program
86
97
  .command("module <module_id>")
87
98
  .description("Ver/editar JSON de un mΓ³dulo")
88
99
  .option("-e, --edit", "Abrir en editor")
89
- .option("-c, --company <id>", "ID de company")
90
100
  .option("-f, --file <path>", "Exportar a archivo")
91
101
  .action(moduleCommand);
92
102
  program
93
103
  .command("module:update <module_id> <json_file>")
94
104
  .description("Actualizar mΓ³dulo desde archivo JSON")
95
- .option("-c, --company <id>", "ID de company")
96
105
  .option("--dry-run", "Validar sin guardar")
97
106
  .action(moduleUpdateCommand);
98
107
  program
@@ -109,10 +118,9 @@ program
109
118
  .option("-c, --company <id>", "ID de company")
110
119
  .action(automationsCommand);
111
120
  program
112
- .command("automation <name>")
121
+ .command("automation <id>")
113
122
  .description("Ver/editar cΓ³digo de automation")
114
123
  .option("-e, --edit", "Abrir en editor")
115
- .option("-c, --company <id>", "ID de company")
116
124
  .option("-f, --file <path>", "Exportar a archivo")
117
125
  .action(automationCommand);
118
126
  program
@@ -120,6 +128,18 @@ program
120
128
  .description("Crear/actualizar automation desde archivo JS")
121
129
  .option("-c, --company <id>", "ID de company (requerido)")
122
130
  .action(automationCreateCommand);
131
+ program
132
+ .command("automations:meta")
133
+ .description("Ver Γ­ndice del worker (debugging)")
134
+ .option("-c, --company <id>", "ID de company")
135
+ .action(automationsMetaCommand);
136
+ program
137
+ .command("automations:executions")
138
+ .description("Ver historial de ejecuciones")
139
+ .option("-c, --company <id>", "ID de company")
140
+ .option("-l, --limit <n>", "Cantidad de registros", "20")
141
+ .option("-s, --script <name>", "Filtrar por script")
142
+ .action(automationsExecutionsCommand);
123
143
  // ════════════════════════════════════════════════════════════════════
124
144
  // πŸ’œ Entity Automations (automations linked to entities)
125
145
  // ════════════════════════════════════════════════════════════════════
@@ -127,13 +147,11 @@ program
127
147
  .command("entity:automations <entity_id>")
128
148
  .description("Ver/editar automations de una entidad")
129
149
  .option("-e, --edit", "Abrir en editor")
130
- .option("-c, --company <id>", "ID de company")
131
150
  .option("-f, --file <path>", "Exportar a archivo")
132
151
  .action(entityAutomationsCommand);
133
152
  program
134
153
  .command("entity:automations:update <entity_id> <json_file>")
135
- .description("Actualizar automations de entidad desde archivo JSON")
136
- .option("-c, --company <id>", "ID de company")
154
+ .description("Actualizar automations de entidad desde archivo")
137
155
  .action(entityAutomationsUpdateCommand);
138
156
  // ════════════════════════════════════════════════════════════════════
139
157
  // πŸ” Environment Variables
@@ -192,30 +210,80 @@ program
192
210
  .option("-f, --file <path>", "Archivo con array de objetos")
193
211
  .action(rowsBulkCreateCommand);
194
212
  // ════════════════════════════════════════════════════════════════════
195
- // 🎨 Views (Marketplace) - Desarrollo local
213
+ // 🎨 View Development - Desarrollo local de vistas
196
214
  // ════════════════════════════════════════════════════════════════════
197
215
  program
198
- .command("views")
199
- .description("Ver mis views del Marketplace")
200
- .action(listCommand);
201
- program
202
- .command("view:pull [name]")
216
+ .command("pull [viewId]")
203
217
  .description("Descargar vista para editar localmente")
204
218
  .action(pullCommand);
205
219
  program
206
- .command("view:push")
220
+ .command("push [path]")
207
221
  .description("Subir cambios locales a Gufi")
208
222
  .action(pushCommand);
209
223
  program
210
- .command("view:watch [dir]")
224
+ .command("watch [path]")
211
225
  .description("Auto-sync al guardar archivos")
212
226
  .action(watchCommand);
213
227
  program
214
- .command("view:status")
228
+ .command("status [path]")
215
229
  .description("Ver estado de sincronizaciΓ³n")
216
230
  .action(statusCommand);
217
231
  program
218
- .command("view:logs [dir]")
232
+ .command("logs [dir]")
219
233
  .description("Ver console.log del LivePreview")
220
234
  .action(logsCommand);
235
+ // ════════════════════════════════════════════════════════════════════
236
+ // πŸ“¦ Packages (Marketplace)
237
+ // ════════════════════════════════════════════════════════════════════
238
+ program
239
+ .command("packages")
240
+ .description("Ver mis packages del Marketplace")
241
+ .action(packagesCommand);
242
+ program
243
+ .command("package <id>")
244
+ .description("Ver detalles de un package")
245
+ .action(packageCommand);
246
+ program
247
+ .command("package:create <name>")
248
+ .description("Crear nuevo package")
249
+ .option("-d, --description <desc>", "DescripciΓ³n del package")
250
+ .action(packageCreateCommand);
251
+ program
252
+ .command("package:delete <id>")
253
+ .description("Eliminar package")
254
+ .action(packageDeleteCommand);
255
+ program
256
+ .command("package:add-module <packageId>")
257
+ .description("AΓ±adir mΓ³dulo a package")
258
+ .option("-c, --company <id>", "ID de company")
259
+ .option("-m, --module <id>", "ID de mΓ³dulo")
260
+ .action(packageAddModuleCommand);
261
+ program
262
+ .command("package:remove-module <packageId> <moduleId>")
263
+ .description("Eliminar mΓ³dulo de package")
264
+ .action(packageRemoveModuleCommand);
265
+ program
266
+ .command("package:publish <id>")
267
+ .description("Publicar package al Marketplace")
268
+ .action(packagePublishCommand);
269
+ program
270
+ .command("package:unpublish <id>")
271
+ .description("Despublicar package")
272
+ .action(packageUnpublishCommand);
273
+ program
274
+ .command("package:sync <id>")
275
+ .description("Sincronizar versiΓ³n del package")
276
+ .action(packageSyncCommand);
277
+ program
278
+ .command("package:check <id>")
279
+ .description("Verificar cambios pendientes")
280
+ .action(packageCheckCommand);
281
+ program
282
+ .command("package:add-view <packageId> <viewId>")
283
+ .description("AΓ±adir view a package")
284
+ .action(packageAddViewCommand);
285
+ program
286
+ .command("package:remove-view <packageId> <packageViewId>")
287
+ .description("Eliminar view de package")
288
+ .action(packageRemoveViewCommand);
221
289
  program.parse();
package/dist/lib/api.js CHANGED
@@ -119,10 +119,18 @@ export async function listViews(packageId) {
119
119
  return request(`/api/marketplace/packages/${packageId}/views`);
120
120
  }
121
121
  export async function getView(viewId) {
122
- return request(`/api/marketplace/views/${viewId}`);
122
+ // πŸ’œ Backend returns { data: View }, unwrap it
123
+ const response = await request(`/api/marketplace/views/${viewId}`);
124
+ return response.data;
123
125
  }
124
126
  export async function getViewFiles(viewId) {
125
- return request(`/api/marketplace/views/${viewId}/files`);
127
+ // πŸ’œ Backend returns { data: ViewFile[], total: number }, unwrap it
128
+ const response = await request(`/api/marketplace/views/${viewId}/files`);
129
+ // Handle both formats: { data: [...] } or direct array
130
+ if (Array.isArray(response)) {
131
+ return response;
132
+ }
133
+ return response.data || [];
126
134
  }
127
135
  export async function saveViewFiles(viewId, files) {
128
136
  await request(`/api/marketplace/views/${viewId}/files/bulk`, {
@@ -56,8 +56,8 @@ export function clearToken() {
56
56
  const config = loadConfig();
57
57
  delete config.token;
58
58
  delete config.refreshToken;
59
- delete config.email;
60
- delete config.password; // πŸ’œ Also clear saved credentials on logout
59
+ // πŸ’œ Keep email and password for auto-login next time
60
+ // Only clear them if user explicitly wants to (not implemented yet)
61
61
  saveConfig(config);
62
62
  }
63
63
  export function isLoggedIn() {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "gufi-cli",
3
- "version": "0.1.13",
3
+ "version": "0.1.17",
4
4
  "description": "CLI for developing Gufi Marketplace views locally with Claude Code",
5
5
  "bin": {
6
6
  "gufi": "./bin/gufi.js"