gufi-cli 0.1.8 → 0.1.9
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 +48 -6
- package/dist/commands/companies.js +3 -2
- package/dist/commands/list.js +2 -2
- package/dist/commands/pull.js +11 -19
- package/dist/index.js +1 -1
- package/dist/lib/sync.d.ts +5 -2
- package/dist/lib/sync.js +10 -6
- package/package.json +1 -1
package/CLAUDE.md
CHANGED
|
@@ -604,10 +604,10 @@ SELECT id, name, code FROM marketplace.views WHERE package_id = 14;
|
|
|
604
604
|
|
|
605
605
|
### Estructura de Archivos de una View
|
|
606
606
|
|
|
607
|
-
Cuando descargas una view con `gufi pull
|
|
607
|
+
Cuando descargas una view con `gufi view:pull <id>`, obtienes:
|
|
608
608
|
|
|
609
609
|
```
|
|
610
|
-
~/gufi-dev/
|
|
610
|
+
~/gufi-dev/view_<id>/
|
|
611
611
|
├── index.tsx # Entry point - exporta featureConfig y default
|
|
612
612
|
├── types.ts # Interfaces TypeScript
|
|
613
613
|
│
|
|
@@ -1032,6 +1032,46 @@ export default function MiVista({ gufi }) {
|
|
|
1032
1032
|
- Permisos dinámicos expandibles (ej: warehouses individuales)
|
|
1033
1033
|
- Integridad total: UI siempre refleja el estado real
|
|
1034
1034
|
|
|
1035
|
+
### 💜 DevPermissionSwitcher AUTOMÁTICO (LivePreviewPage)
|
|
1036
|
+
|
|
1037
|
+
**¡Ya no necesitas incluir DevPermissionSwitcher en tu código!** LivePreviewPage detecta automáticamente si tu vista tiene `featureConfig.permissions` y muestra el botón DEV.
|
|
1038
|
+
|
|
1039
|
+
**Cómo funciona:**
|
|
1040
|
+
1. Declara permisos en `metadata/permissions.ts`
|
|
1041
|
+
2. Expórtalos en `featureConfig`:
|
|
1042
|
+
```typescript
|
|
1043
|
+
// core/dataProvider.ts
|
|
1044
|
+
import { permissions } from '../metadata/permissions';
|
|
1045
|
+
|
|
1046
|
+
export const featureConfig = {
|
|
1047
|
+
dataSources,
|
|
1048
|
+
inputs: featureInputs,
|
|
1049
|
+
permissions, // 💜 Solo añadir esto
|
|
1050
|
+
};
|
|
1051
|
+
```
|
|
1052
|
+
3. LivePreviewPage lo detecta y muestra el botón DEV automáticamente
|
|
1053
|
+
|
|
1054
|
+
**Usar devPermissions del contexto:**
|
|
1055
|
+
```typescript
|
|
1056
|
+
export default function MiVista({ gufi }) {
|
|
1057
|
+
// 💜 LivePreviewPage provee devPermissions automáticamente
|
|
1058
|
+
const effectiveDevPermissions = gufi?.context?.devPermissions || [];
|
|
1059
|
+
|
|
1060
|
+
const permissionsConfig = {
|
|
1061
|
+
userPermissions: gufi?.context?.userPermissions || [],
|
|
1062
|
+
isDevMode: gufi?.context?.isPreview || gufi?.context?.isDev,
|
|
1063
|
+
devPermissions: effectiveDevPermissions,
|
|
1064
|
+
};
|
|
1065
|
+
|
|
1066
|
+
// Usar helpers de permisos normalmente
|
|
1067
|
+
const canSend = hasPermission(permissionsConfig, 'send_orders');
|
|
1068
|
+
}
|
|
1069
|
+
```
|
|
1070
|
+
|
|
1071
|
+
**Cuándo incluir DevPermissionSwitcher manualmente:**
|
|
1072
|
+
- Solo si la vista se ejecuta fuera de LivePreviewPage (ej: vista nativa del frontend)
|
|
1073
|
+
- Para vistas del marketplace en Developer Center → **NO necesario**, es automático
|
|
1074
|
+
|
|
1035
1075
|
**Principio clave**: Inicializar con `[]` vacío, luego usar `useEffect` para otorgar todos los permisos cuando los valores dinámicos (como warehouses) terminen de cargar. Esto garantiza integridad UI.
|
|
1036
1076
|
|
|
1037
1077
|
---
|
|
@@ -1629,11 +1669,13 @@ gufi rows:create m360_t16192 --file datos.json
|
|
|
1629
1669
|
### Desarrollo de Views
|
|
1630
1670
|
|
|
1631
1671
|
```bash
|
|
1632
|
-
# Ver tus views del Marketplace
|
|
1672
|
+
# Ver tus views del Marketplace (muestra ID de cada vista)
|
|
1633
1673
|
gufi views
|
|
1674
|
+
# 📦 Gestión de Tareas (ID: 14)
|
|
1675
|
+
# └─ 13 Tasks Manager (custom)
|
|
1634
1676
|
|
|
1635
|
-
# Descargar view
|
|
1636
|
-
gufi view:pull
|
|
1677
|
+
# Descargar view por ID (se guarda en ~/gufi-dev/view_<id>/)
|
|
1678
|
+
gufi view:pull 13
|
|
1637
1679
|
|
|
1638
1680
|
# Auto-sync al guardar archivos
|
|
1639
1681
|
gufi view:watch
|
|
@@ -1670,7 +1712,7 @@ gufi view:status
|
|
|
1670
1712
|
| Ver estructura de una company | `gufi modules <company_id>` |
|
|
1671
1713
|
| Ver/editar JSON de módulo | `gufi module <id> -c <company>` |
|
|
1672
1714
|
| Ver/editar código de automation | `gufi automation <nombre> -c <company>` |
|
|
1673
|
-
| Desarrollar una vista | `gufi pull
|
|
1715
|
+
| Desarrollar una vista | `gufi view:pull <id>`, `gufi view:watch`, `gufi view:logs` |
|
|
1674
1716
|
|
|
1675
1717
|
### Errores comunes
|
|
1676
1718
|
|
|
@@ -370,11 +370,12 @@ export async function companyCreateCommand(name) {
|
|
|
370
370
|
method: "POST",
|
|
371
371
|
body: JSON.stringify({ name }),
|
|
372
372
|
});
|
|
373
|
-
|
|
373
|
+
// API returns { company: {...}, message: "..." }
|
|
374
|
+
const company = result.company || result.data || result;
|
|
374
375
|
console.log(chalk.green(" ✓ Company creada!\n"));
|
|
375
376
|
console.log(chalk.white(` ID: ${company.id}`));
|
|
376
377
|
console.log(chalk.white(` Nombre: ${company.name}`));
|
|
377
|
-
console.log(chalk.white(` Schema:
|
|
378
|
+
console.log(chalk.white(` Schema: ${company.schema || 'company_' + company.id}`));
|
|
378
379
|
console.log(chalk.gray("\n 💡 Usa: gufi modules " + company.id + " para ver módulos\n"));
|
|
379
380
|
}
|
|
380
381
|
catch (err) {
|
package/dist/commands/list.js
CHANGED
|
@@ -29,11 +29,11 @@ export async function listCommand() {
|
|
|
29
29
|
else {
|
|
30
30
|
views.forEach((view, i) => {
|
|
31
31
|
const prefix = i === views.length - 1 ? "└─" : "├─";
|
|
32
|
-
console.log(
|
|
32
|
+
console.log(` ${prefix} ${chalk.cyan(view.pk_id)} ${view.name} ${chalk.gray(`(${view.view_type})`)}`);
|
|
33
33
|
});
|
|
34
34
|
}
|
|
35
35
|
}
|
|
36
|
-
console.log(chalk.gray("\n Usa: gufi pull <
|
|
36
|
+
console.log(chalk.gray("\n Usa: ") + chalk.cyan("gufi view:pull <id>") + chalk.gray(" para descargar una vista\n"));
|
|
37
37
|
}
|
|
38
38
|
catch (error) {
|
|
39
39
|
spinner.fail(chalk.red(error.message));
|
package/dist/commands/pull.js
CHANGED
|
@@ -13,17 +13,17 @@ export async function pullCommand(viewIdentifier) {
|
|
|
13
13
|
}
|
|
14
14
|
console.log(chalk.magenta("\n 🟣 Gufi Pull\n"));
|
|
15
15
|
let viewId;
|
|
16
|
-
let
|
|
16
|
+
let viewKey;
|
|
17
17
|
let packageId;
|
|
18
|
-
//
|
|
18
|
+
// 💜 Solo acepta ID numérico
|
|
19
19
|
if (viewIdentifier && /^\d+$/.test(viewIdentifier)) {
|
|
20
20
|
viewId = parseInt(viewIdentifier);
|
|
21
21
|
const spinner = ora("Obteniendo vista...").start();
|
|
22
22
|
try {
|
|
23
23
|
const view = await getView(viewId);
|
|
24
|
-
|
|
24
|
+
viewKey = `view_${viewId}`;
|
|
25
25
|
packageId = view.package_id;
|
|
26
|
-
spinner.succeed(`Vista: ${
|
|
26
|
+
spinner.succeed(`Vista ${chalk.cyan(viewId)}: ${view.name || "sin nombre"}`);
|
|
27
27
|
}
|
|
28
28
|
catch (error) {
|
|
29
29
|
spinner.fail(chalk.red(`No se encontró la vista ${viewId}`));
|
|
@@ -56,22 +56,14 @@ export async function pullCommand(viewIdentifier) {
|
|
|
56
56
|
process.exit(0);
|
|
57
57
|
}
|
|
58
58
|
console.log(chalk.gray(" Vistas disponibles:\n"));
|
|
59
|
-
views.forEach((view
|
|
60
|
-
|
|
61
|
-
console.log(` ${chalk.cyan(i + 1)}. ${name} ${chalk.gray(`(${view.view_type})`)}`);
|
|
59
|
+
views.forEach((view) => {
|
|
60
|
+
console.log(` ${chalk.cyan(view.pk_id)} ${view.name || "sin nombre"} ${chalk.gray(`(${view.view_type})`)}`);
|
|
62
61
|
});
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
? views.find(v => v.name && v.name.toLowerCase().includes(viewIdentifier.toLowerCase()))
|
|
66
|
-
: views[0];
|
|
67
|
-
if (!selectedView) {
|
|
68
|
-
console.log(chalk.yellow(`\n No se encontró vista "${viewIdentifier}"\n`));
|
|
69
|
-
console.log(chalk.gray(" Uso: gufi pull <nombre-vista> o gufi pull <view-id>\n"));
|
|
70
|
-
process.exit(1);
|
|
62
|
+
if (viewIdentifier) {
|
|
63
|
+
console.log(chalk.yellow(`\n "${viewIdentifier}" no es un ID válido.\n`));
|
|
71
64
|
}
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
console.log(chalk.gray(`\n Descargando: ${viewName}\n`));
|
|
65
|
+
console.log(chalk.gray("\n Uso: ") + chalk.cyan("gufi view:pull <id>") + chalk.gray(" (ej: gufi view:pull 13)\n"));
|
|
66
|
+
process.exit(0);
|
|
75
67
|
}
|
|
76
68
|
catch (error) {
|
|
77
69
|
spinner.fail(chalk.red(error.message));
|
|
@@ -81,7 +73,7 @@ export async function pullCommand(viewIdentifier) {
|
|
|
81
73
|
// Pull the view
|
|
82
74
|
const pullSpinner = ora("Descargando archivos...").start();
|
|
83
75
|
try {
|
|
84
|
-
const result = await pullView(viewId,
|
|
76
|
+
const result = await pullView(viewId, viewKey, packageId);
|
|
85
77
|
pullSpinner.succeed(chalk.green(`${result.fileCount} archivos descargados`));
|
|
86
78
|
console.log(chalk.gray(`\n 📁 ${result.dir}\n`));
|
|
87
79
|
console.log(chalk.gray(" Comandos útiles:"));
|
package/dist/index.js
CHANGED
|
@@ -44,7 +44,7 @@ const program = new Command();
|
|
|
44
44
|
program
|
|
45
45
|
.name("gufi")
|
|
46
46
|
.description("🟣 Gufi CLI - Desarrolla módulos, vistas y automations")
|
|
47
|
-
.version("0.1.
|
|
47
|
+
.version("0.1.8");
|
|
48
48
|
// ════════════════════════════════════════════════════════════════════
|
|
49
49
|
// 🔐 Auth
|
|
50
50
|
// ════════════════════════════════════════════════════════════════════
|
package/dist/lib/sync.d.ts
CHANGED
|
@@ -12,12 +12,15 @@ export interface ViewMeta {
|
|
|
12
12
|
mtime: number;
|
|
13
13
|
}>;
|
|
14
14
|
}
|
|
15
|
-
export declare function getViewDir(
|
|
15
|
+
export declare function getViewDir(viewKey: string): string;
|
|
16
16
|
export declare function loadViewMeta(viewDir: string): ViewMeta | null;
|
|
17
17
|
/**
|
|
18
18
|
* Pull view files from Gufi to local directory
|
|
19
|
+
* @param viewId - The view ID
|
|
20
|
+
* @param viewKey - The unique identifier in view_<id> format (e.g., view_123)
|
|
21
|
+
* @param packageId - The package ID
|
|
19
22
|
*/
|
|
20
|
-
export declare function pullView(viewId: number,
|
|
23
|
+
export declare function pullView(viewId: number, viewKey: string, packageId: number): Promise<{
|
|
21
24
|
dir: string;
|
|
22
25
|
fileCount: number;
|
|
23
26
|
}>;
|
package/dist/lib/sync.js
CHANGED
|
@@ -37,8 +37,9 @@ function getLanguage(filePath) {
|
|
|
37
37
|
};
|
|
38
38
|
return langMap[ext] || "text";
|
|
39
39
|
}
|
|
40
|
-
|
|
41
|
-
|
|
40
|
+
// 💜 viewKey is now always in view_<id> format (e.g., view_123)
|
|
41
|
+
export function getViewDir(viewKey) {
|
|
42
|
+
return path.join(GUFI_DEV_DIR, viewKey.toLowerCase());
|
|
42
43
|
}
|
|
43
44
|
export function loadViewMeta(viewDir) {
|
|
44
45
|
const metaPath = path.join(viewDir, META_FILE);
|
|
@@ -57,9 +58,12 @@ function saveViewMeta(viewDir, meta) {
|
|
|
57
58
|
}
|
|
58
59
|
/**
|
|
59
60
|
* Pull view files from Gufi to local directory
|
|
61
|
+
* @param viewId - The view ID
|
|
62
|
+
* @param viewKey - The unique identifier in view_<id> format (e.g., view_123)
|
|
63
|
+
* @param packageId - The package ID
|
|
60
64
|
*/
|
|
61
|
-
export async function pullView(viewId,
|
|
62
|
-
const viewDir = getViewDir(
|
|
65
|
+
export async function pullView(viewId, viewKey, packageId) {
|
|
66
|
+
const viewDir = getViewDir(viewKey);
|
|
63
67
|
ensureDir(viewDir);
|
|
64
68
|
const files = await getViewFiles(viewId);
|
|
65
69
|
const fileMeta = {};
|
|
@@ -74,14 +78,14 @@ export async function pullView(viewId, viewName, packageId) {
|
|
|
74
78
|
}
|
|
75
79
|
const meta = {
|
|
76
80
|
viewId,
|
|
77
|
-
viewName,
|
|
81
|
+
viewName: viewKey, // 💜 Store viewKey as viewName for backwards compat
|
|
78
82
|
packageId,
|
|
79
83
|
lastSync: new Date().toISOString(),
|
|
80
84
|
files: fileMeta,
|
|
81
85
|
};
|
|
82
86
|
saveViewMeta(viewDir, meta);
|
|
83
87
|
// Update current view in config
|
|
84
|
-
setCurrentView({ id: viewId, name:
|
|
88
|
+
setCurrentView({ id: viewId, name: viewKey, packageId, localPath: viewDir });
|
|
85
89
|
return { dir: viewDir, fileCount: files.length };
|
|
86
90
|
}
|
|
87
91
|
/**
|